Skip to content

Commit b2db864

Browse files
Merge branch 'master' into ft/authorize_storage
2 parents 239452a + c897382 commit b2db864

File tree

9 files changed

+363
-19
lines changed

9 files changed

+363
-19
lines changed

SoftLayer/CLI/hardware/upgrade.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
"""Upgrade a hardware server."""
2+
# :license: MIT, see LICENSE for more details.
3+
4+
import click
5+
6+
import SoftLayer
7+
from SoftLayer.CLI import environment
8+
from SoftLayer.CLI import exceptions
9+
from SoftLayer.CLI import formatting
10+
from SoftLayer.CLI import helpers
11+
12+
13+
@click.command()
14+
@click.argument('identifier')
15+
@click.option('--memory', type=click.INT, help="Memory Size in GB")
16+
@click.option('--network', help="Network port speed in Mbps",
17+
default=None,
18+
type=click.Choice(['100', '100 Redundant', '100 Dual',
19+
'1000', '1000 Redundant', '1000 Dual',
20+
'10000', '10000 Redundant', '10000 Dual'])
21+
)
22+
@click.option('--drive-controller',
23+
help="Drive Controller",
24+
default=None,
25+
type=click.Choice(['Non-RAID', 'RAID']))
26+
@click.option('--public-bandwidth', type=click.INT, help="Public Bandwidth in GB")
27+
@click.option('--test', is_flag=True, default=False, help="Do not actually upgrade the hardware server")
28+
@environment.pass_env
29+
def cli(env, identifier, memory, network, drive_controller, public_bandwidth, test):
30+
"""Upgrade a Hardware Server."""
31+
32+
mgr = SoftLayer.HardwareManager(env.client)
33+
34+
if not any([memory, network, drive_controller, public_bandwidth]):
35+
raise exceptions.ArgumentError("Must provide "
36+
" [--memory], [--network], [--drive-controller], or [--public-bandwidth]")
37+
38+
hw_id = helpers.resolve_id(mgr.resolve_ids, identifier, 'Hardware')
39+
if not test:
40+
if not (env.skip_confirmations or formatting.confirm(
41+
"This action will incur charges on your account. Continue?")):
42+
raise exceptions.CLIAbort('Aborted')
43+
44+
if not mgr.upgrade(hw_id, memory=memory, nic_speed=network, drive_controller=drive_controller,
45+
public_bandwidth=public_bandwidth, test=test):
46+
raise exceptions.CLIAbort('Hardware Server Upgrade Failed')
47+
env.fout('Successfully Upgraded.')

SoftLayer/CLI/routes.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,7 @@
259259
('hardware:authorize-storage', 'SoftLayer.CLI.hardware.authorize_storage:cli'),
260260
('hardware:dns-sync', 'SoftLayer.CLI.hardware.dns:cli'),
261261
('hardware:storage', 'SoftLayer.CLI.hardware.storage:cli'),
262+
('hardware:upgrade', 'SoftLayer.CLI.hardware.upgrade:cli'),
262263

263264
('securitygroup', 'SoftLayer.CLI.securitygroup'),
264265
('securitygroup:list', 'SoftLayer.CLI.securitygroup.list:cli'),

SoftLayer/CLI/virt/detail.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,10 @@ def cli(env, identifier, passwords=False, price=False):
6969
table.add_row(['transient', result.get('transientGuestFlag', False)])
7070
table.add_row(['created', result['createDate']])
7171
table.add_row(['modified', result['modifyDate']])
72+
table.add_row(['preset', utils.lookup(result, 'billingItem',
73+
'orderItem',
74+
'preset',
75+
'keyName') or '-'])
7276

7377
table.add_row(_get_owner_row(result))
7478
table.add_row(_get_vlan_table(result))

SoftLayer/fixtures/SoftLayer_Hardware_Server.py

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
'billingItem': {
77
'id': 6327,
88
'recurringFee': 1.54,
9+
'package': {
10+
'id': 911
11+
},
912
'nextInvoiceTotalRecurringAmount': 16.08,
1013
'children': [
1114
{'description': 'test', 'nextInvoiceTotalRecurringAmount': 1},
@@ -262,3 +265,76 @@
262265
}
263266
}
264267
}
268+
269+
getUpgradeItemPrices = [
270+
{
271+
"id": 21525,
272+
"recurringFee": "0",
273+
"categories": [
274+
{
275+
"categoryCode": "port_speed",
276+
"id": 26,
277+
"name": "Uplink Port Speeds",
278+
}
279+
],
280+
"item": {
281+
"capacity": "10000",
282+
"description": "10 Gbps Redundant Public & Private Network Uplinks",
283+
"id": 4342,
284+
"keyName": "10_GBPS_REDUNDANT_PUBLIC_PRIVATE_NETWORK_UPLINKS"
285+
}
286+
},
287+
{
288+
"hourlyRecurringFee": ".247",
289+
"id": 209391,
290+
"recurringFee": "164",
291+
"categories": [
292+
{
293+
"categoryCode": "ram",
294+
"id": 3,
295+
"name": "RAM"
296+
}
297+
],
298+
"item": {
299+
"capacity": "32",
300+
"description": "32 GB RAM",
301+
"id": 11291,
302+
"keyName": "RAM_32_GB_DDR4_2133_ECC_NON_REG"
303+
}
304+
},
305+
{
306+
"hourlyRecurringFee": ".068",
307+
"id": 22482,
308+
"recurringFee": "50",
309+
"categories": [
310+
{
311+
"categoryCode": "disk_controller",
312+
"id": 11,
313+
"name": "Disk Controller",
314+
}
315+
],
316+
"item": {
317+
"capacity": "0",
318+
"description": "RAID",
319+
"id": 4478,
320+
"keyName": "DISK_CONTROLLER_RAID",
321+
}
322+
},
323+
{
324+
"id": 50357,
325+
"recurringFee": "0",
326+
"categories": [
327+
{
328+
"categoryCode": "bandwidth",
329+
"id": 10,
330+
"name": "Public Bandwidth",
331+
}
332+
],
333+
"item": {
334+
"capacity": "500",
335+
"description": "500 GB Bandwidth Allotment",
336+
"id": 6177,
337+
"keyName": "BANDWIDTH_500_GB"
338+
}
339+
}
340+
]

SoftLayer/fixtures/SoftLayer_Virtual_Guest.py

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@
2828
'userRecord': {
2929
'username': 'chechu',
3030
}
31-
}
31+
},
32+
'preset': {'keyName': 'B1_8X16X100'}
3233
}
3334
},
3435
'datacenter': {'id': 50, 'name': 'TEST00',
@@ -480,30 +481,30 @@
480481
"categoryCode": "guest_disk0",
481482
"id": 81
482483
}}, {
483-
"description": "250 GB (SAN)",
484-
"attributes": [
485-
{
486-
"id": 198,
487-
"attributeTypeKeyName": "SAN_DISK"
488-
}],
489-
"itemCategory": {
490-
"categoryCode": "guest_disk0",
491-
"id": 89
492-
}}],
484+
"description": "250 GB (SAN)",
485+
"attributes": [
486+
{
487+
"id": 198,
488+
"attributeTypeKeyName": "SAN_DISK"
489+
}],
490+
"itemCategory": {
491+
"categoryCode": "guest_disk0",
492+
"id": 89
493+
}}],
493494
'guest_core': [{
494495
"description": "4 x 2.0 GHz or higher Cores (Dedicated)",
495496
"attributes": [],
496497
"itemCategory": {
497498
"categoryCode": "guest_core",
498499
"id": 80
499500
}},
500-
{
501-
"description": "8 x 2.0 GHz or higher Cores",
502-
"attributes": [],
503-
"itemCategory": {
504-
"categoryCode": "guest_core",
505-
"id": 90
506-
}}]
501+
{
502+
"description": "8 x 2.0 GHz or higher Cores",
503+
"attributes": [],
504+
"itemCategory": {
505+
"categoryCode": "guest_core",
506+
"id": 90
507+
}}]
507508
}
508509

509510
getReverseDomainRecords = [{

SoftLayer/managers/hardware.py

Lines changed: 142 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@
55
66
:license: MIT, see LICENSE for more details.
77
"""
8+
import datetime
89
import logging
910
import socket
1011
import time
1112

1213
from SoftLayer.decoration import retry
14+
from SoftLayer import exceptions
1315
from SoftLayer.exceptions import SoftLayerError
1416
from SoftLayer.managers import ordering
1517
from SoftLayer.managers.ticket import TicketManager
@@ -18,7 +20,7 @@
1820
LOGGER = logging.getLogger(__name__)
1921

2022
# Invalid names are ignored due to long method names and short argument names
21-
# pylint: disable=invalid-name, no-self-use
23+
# pylint: disable=invalid-name, no-self-use, too-many-lines
2224

2325
EXTRA_CATEGORIES = ['pri_ipv6_addresses',
2426
'static_ipv6_addresses',
@@ -814,6 +816,145 @@ def authorize_storage(self, hardware_id, username_storage):
814816

815817
return result
816818

819+
def upgrade(self, instance_id, memory=None,
820+
nic_speed=None, drive_controller=None,
821+
public_bandwidth=None, test=False):
822+
"""Upgrades a hardware server instance.
823+
824+
:param int instance_id: Instance id of the hardware server to be upgraded.
825+
:param int memory: Memory size.
826+
:param string nic_speed: Network Port Speed data.
827+
:param string drive_controller: Drive Controller data.
828+
:param int public_bandwidth: Public keyName data.
829+
:param bool test: Test option to verify the request.
830+
831+
:returns: bool
832+
"""
833+
upgrade_prices = self._get_upgrade_prices(instance_id)
834+
prices = []
835+
data = {}
836+
837+
if memory:
838+
data['memory'] = memory
839+
if nic_speed:
840+
data['nic_speed'] = nic_speed
841+
if drive_controller:
842+
data['disk_controller'] = drive_controller
843+
if public_bandwidth:
844+
data['bandwidth'] = public_bandwidth
845+
846+
server_response = self.get_instance(instance_id)
847+
package_id = server_response['billingItem']['package']['id']
848+
849+
maintenance_window = datetime.datetime.now(utils.UTC())
850+
order = {
851+
'complexType': 'SoftLayer_Container_Product_Order_Hardware_Server_Upgrade',
852+
'properties': [{
853+
'name': 'MAINTENANCE_WINDOW',
854+
'value': maintenance_window.strftime("%Y-%m-%d %H:%M:%S%z")
855+
}],
856+
'hardware': [{'id': int(instance_id)}],
857+
'packageId': package_id
858+
}
859+
860+
for option, value in data.items():
861+
price_id = self._get_prices_for_upgrade_option(upgrade_prices, option, value)
862+
if not price_id:
863+
# Every option provided is expected to have a price
864+
raise exceptions.SoftLayerError(
865+
"Unable to find %s option with value %s" % (option, value))
866+
867+
prices.append({'id': price_id})
868+
869+
order['prices'] = prices
870+
871+
if prices:
872+
if test:
873+
self.client['Product_Order'].verifyOrder(order)
874+
else:
875+
self.client['Product_Order'].placeOrder(order)
876+
return True
877+
return False
878+
879+
@retry(logger=LOGGER)
880+
def get_instance(self, instance_id):
881+
"""Get details about a hardware server instance.
882+
883+
:param int instance_id: the instance ID
884+
:returns: A dictionary containing a large amount of information about
885+
the specified instance.
886+
"""
887+
mask = [
888+
'billingItem[id,package[id,keyName]]'
889+
]
890+
mask = "mask[%s]" % ','.join(mask)
891+
892+
return self.hardware.getObject(id=instance_id, mask=mask)
893+
894+
def _get_upgrade_prices(self, instance_id, include_downgrade_options=True):
895+
"""Following Method gets all the price ids related to upgrading a Hardware Server.
896+
897+
:param int instance_id: Instance id of the Hardware Server to be upgraded.
898+
899+
:returns: list
900+
"""
901+
mask = [
902+
'id',
903+
'locationGroupId',
904+
'categories[name,id,categoryCode]',
905+
'item[keyName,description,capacity,units]'
906+
]
907+
mask = "mask[%s]" % ','.join(mask)
908+
return self.hardware.getUpgradeItemPrices(include_downgrade_options, id=instance_id, mask=mask)
909+
910+
@staticmethod
911+
def _get_prices_for_upgrade_option(upgrade_prices, option, value):
912+
"""Find the price id for the option and value to upgrade. This
913+
914+
:param list upgrade_prices: Contains all the prices related to a
915+
hardware server upgrade.
916+
:param string option: Describes type of parameter to be upgraded
917+
918+
:return: int.
919+
"""
920+
price_id = None
921+
option_category = {
922+
'memory': 'ram',
923+
'nic_speed': 'port_speed',
924+
'disk_controller': 'disk_controller',
925+
'bandwidth': 'bandwidth'
926+
}
927+
category_code = option_category.get(option)
928+
929+
for price in upgrade_prices:
930+
if price.get('categories') is None or price.get('item') is None:
931+
continue
932+
933+
product = price.get('item')
934+
for category in price.get('categories'):
935+
if not category.get('categoryCode') == category_code:
936+
continue
937+
938+
if option == 'disk_controller':
939+
if value == product.get('description'):
940+
price_id = price.get('id')
941+
elif option == 'nic_speed':
942+
if value.isdigit():
943+
if str(product.get('capacity')) == str(value):
944+
price_id = price.get('id')
945+
else:
946+
split_nic_speed = value.split(" ")
947+
if str(product.get('capacity')) == str(split_nic_speed[0]) and \
948+
split_nic_speed[1] in product.get("description"):
949+
price_id = price.get('id')
950+
elif option == 'bandwidth':
951+
if str(product.get('capacity')) == str(value):
952+
price_id = price.get('id')
953+
else:
954+
if str(product.get('capacity')) == str(value):
955+
price_id = price.get('id')
956+
957+
return price_id
817958

818959
def _get_bandwidth_key(items, hourly=True, no_public=False, location=None):
819960
"""Picks a valid Bandwidth Item, returns the KeyName"""

docs/cli/hardware.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,3 +123,7 @@ This function updates the firmware of a server. If already at the latest version
123123
.. click:: SoftLayer.CLI.hardware.authorize_storage:cli
124124
:prog: hardware authorize-storage
125125
:show-nested:
126+
127+
.. click:: SoftLayer.CLI.hardware.upgrade:cli
128+
:prog: hardware upgrade
129+
:show-nested:

0 commit comments

Comments
 (0)