Skip to content

Commit 1e35e22

Browse files
Merge pull request #1153 from camporter/transient_vsi
Transient VSI support
2 parents 4ec5501 + db50af5 commit 1e35e22

File tree

9 files changed

+161
-33
lines changed

9 files changed

+161
-33
lines changed

SoftLayer/CLI/virt/create.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ def _update_with_like_args(ctx, _, value):
3333
'dedicated': like_details['dedicatedAccountHostOnlyFlag'],
3434
'private': like_details['privateNetworkOnlyFlag'],
3535
'placement_id': like_details.get('placementGroupId', None),
36+
'transient': like_details.get('transientGuestFlag', None),
3637
}
3738

3839
like_args['flavor'] = utils.lookup(like_details,
@@ -83,6 +84,7 @@ def _parse_create_args(client, args):
8384
"domain": args.get('domain', None),
8485
"host_id": args.get('host_id', None),
8586
"private": args.get('private', None),
87+
"transient": args.get('transient', None),
8688
"hostname": args.get('hostname', None),
8789
"nic_speed": args.get('network', None),
8890
"boot_mode": args.get('boot_mode', None),
@@ -105,7 +107,8 @@ def _parse_create_args(client, args):
105107
if args.get('image'):
106108
if args.get('image').isdigit():
107109
image_mgr = SoftLayer.ImageManager(client)
108-
image_details = image_mgr.get_image(args.get('image'), mask="id,globalIdentifier")
110+
image_details = image_mgr.get_image(args.get('image'),
111+
mask="id,globalIdentifier")
109112
data['image_id'] = image_details['globalIdentifier']
110113
else:
111114
data['image_id'] = args['image']
@@ -198,6 +201,8 @@ def _parse_create_args(client, args):
198201
@click.option('--placementgroup',
199202
help="Placement Group name or Id to order this guest on. See: slcli vs placementgroup list")
200203
@click.option('--ipv6', is_flag=True, help="Adds an IPv6 address to this guest")
204+
@click.option('--transient', is_flag=True,
205+
help="Create a transient virtual server")
201206
@environment.pass_env
202207
def cli(env, **args):
203208
"""Order/create virtual servers."""
@@ -281,6 +286,10 @@ def _validate_args(env, args):
281286
raise exceptions.ArgumentError(
282287
'[-m | --memory] not allowed with [-f | --flavor]')
283288

289+
if all([args['dedicated'], args['transient']]):
290+
raise exceptions.ArgumentError(
291+
'[--dedicated] not allowed with [--transient]')
292+
284293
if all([args['dedicated'], args['flavor']]):
285294
raise exceptions.ArgumentError(
286295
'[-d | --dedicated] not allowed with [-f | --flavor]')
@@ -289,6 +298,10 @@ def _validate_args(env, args):
289298
raise exceptions.ArgumentError(
290299
'[-h | --host-id] not allowed with [-f | --flavor]')
291300

301+
if args['transient'] and args['billing'] == 'monthly':
302+
raise exceptions.ArgumentError(
303+
'[--transient] not allowed with [--billing monthly]')
304+
292305
if all([args['userdata'], args['userfile']]):
293306
raise exceptions.ArgumentError(
294307
'[-u | --userdata] not allowed with [-F | --userfile]')

SoftLayer/CLI/virt/create_options.py

Lines changed: 35 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
from SoftLayer import utils
1313

1414

15-
@click.command()
15+
@click.command(short_help="Get options to use for creating virtual servers.")
1616
@environment.pass_env
1717
def cli(env):
1818
"""Virtual server order options."""
@@ -32,27 +32,7 @@ def cli(env):
3232
table.add_row(['datacenter',
3333
formatting.listing(datacenters, separator='\n')])
3434

35-
def _add_flavor_rows(flavor_key, flavor_label, flavor_options):
36-
flavors = []
37-
38-
for flavor_option in flavor_options:
39-
flavor_key_name = utils.lookup(flavor_option, 'flavor', 'keyName')
40-
if not flavor_key_name.startswith(flavor_key):
41-
continue
42-
43-
flavors.append(flavor_key_name)
44-
45-
if len(flavors) > 0:
46-
table.add_row(['flavors (%s)' % flavor_label,
47-
formatting.listing(flavors, separator='\n')])
48-
49-
if result.get('flavors', None):
50-
_add_flavor_rows('B1', 'balanced', result['flavors'])
51-
_add_flavor_rows('BL1', 'balanced local - hdd', result['flavors'])
52-
_add_flavor_rows('BL2', 'balanced local - ssd', result['flavors'])
53-
_add_flavor_rows('C1', 'compute', result['flavors'])
54-
_add_flavor_rows('M1', 'memory', result['flavors'])
55-
_add_flavor_rows('AC', 'GPU', result['flavors'])
35+
_add_flavors_to_table(result, table)
5636

5737
# CPUs
5838
standard_cpus = [int(x['template']['startCpus']) for x in result['processors']
@@ -167,3 +147,36 @@ def add_block_rows(disks, name):
167147
formatting.listing(ded_host_speeds, separator=',')])
168148

169149
env.fout(table)
150+
151+
152+
def _add_flavors_to_table(result, table):
153+
grouping = {
154+
'balanced': {'key_starts_with': 'B1', 'flavors': []},
155+
'balanced local - hdd': {'key_starts_with': 'BL1', 'flavors': []},
156+
'balanced local - ssd': {'key_starts_with': 'BL2', 'flavors': []},
157+
'compute': {'key_starts_with': 'C1', 'flavors': []},
158+
'memory': {'key_starts_with': 'M1', 'flavors': []},
159+
'GPU': {'key_starts_with': 'AC', 'flavors': []},
160+
'transient': {'transient': True, 'flavors': []},
161+
}
162+
163+
if result.get('flavors', None) is None:
164+
return
165+
166+
for flavor_option in result['flavors']:
167+
flavor_key_name = utils.lookup(flavor_option, 'flavor', 'keyName')
168+
169+
for name, group in grouping.items():
170+
if utils.lookup(flavor_option, 'template', 'transientGuestFlag') is True:
171+
if utils.lookup(group, 'transient') is True:
172+
group['flavors'].append(flavor_key_name)
173+
break
174+
175+
elif utils.lookup(group, 'key_starts_with') is not None \
176+
and flavor_key_name.startswith(group['key_starts_with']):
177+
group['flavors'].append(flavor_key_name)
178+
break
179+
180+
for name, group in grouping.items():
181+
if len(group['flavors']) > 0:
182+
table.add_row(['flavors (%s)' % name, formatting.listing(group['flavors'], separator='\n')])

SoftLayer/CLI/virt/detail.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ def cli(env, identifier, passwords=False, price=False):
6161
table.add_row(['private_ip', result.get('primaryBackendIpAddress', '-')])
6262
table.add_row(['private_only', result['privateNetworkOnlyFlag']])
6363
table.add_row(['private_cpu', result['dedicatedAccountHostOnlyFlag']])
64+
table.add_row(['transient', result.get('transientGuestFlag', False)])
6465
table.add_row(['created', result['createDate']])
6566
table.add_row(['modified', result['modifyDate']])
6667

SoftLayer/CLI/virt/list.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242
]
4343

4444

45-
@click.command()
45+
@click.command(short_help="List virtual servers.")
4646
@click.option('--cpu', '-c', help='Number of CPU cores', type=click.INT)
4747
@click.option('--domain', '-D', help='Domain portion of the FQDN')
4848
@click.option('--datacenter', '-d', help='Datacenter shortname')
@@ -51,6 +51,7 @@
5151
@click.option('--network', '-n', help='Network port speed in Mbps')
5252
@click.option('--hourly', is_flag=True, help='Show only hourly instances')
5353
@click.option('--monthly', is_flag=True, help='Show only monthly instances')
54+
@click.option('--transient', help='Filter by transient instances', type=click.BOOL)
5455
@helpers.multi_option('--tag', help='Filter by tags')
5556
@click.option('--sortby',
5657
help='Column to sort by',
@@ -68,7 +69,7 @@
6869
show_default=True)
6970
@environment.pass_env
7071
def cli(env, sortby, cpu, domain, datacenter, hostname, memory, network,
71-
hourly, monthly, tag, columns, limit):
72+
hourly, monthly, tag, columns, limit, transient):
7273
"""List virtual servers."""
7374

7475
vsi = SoftLayer.VSManager(env.client)
@@ -80,6 +81,7 @@ def cli(env, sortby, cpu, domain, datacenter, hostname, memory, network,
8081
memory=memory,
8182
datacenter=datacenter,
8283
nic_speed=network,
84+
transient=transient,
8385
tags=tag,
8486
mask=columns.mask(),
8587
limit=limit)

SoftLayer/fixtures/SoftLayer_Virtual_Guest.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
'vlanNumber': 23,
5050
'id': 1}],
5151
'dedicatedHost': {'id': 37401},
52+
'transientGuestFlag': False,
5253
'operatingSystem': {
5354
'passwords': [{'username': 'user', 'password': 'pass'}],
5455
'softwareLicense': {
@@ -75,6 +76,17 @@
7576
}
7677
}
7778
},
79+
{
80+
'flavor': {
81+
'keyName': 'B1_1X2X25_TRANSIENT'
82+
},
83+
'template': {
84+
'supplementalCreateObjectOptions': {
85+
'flavorKeyName': 'B1_1X2X25_TRANSIENT'
86+
},
87+
'transientGuestFlag': True
88+
}
89+
},
7890
{
7991
'flavor': {
8092
'keyName': 'B1_1X2X100'

SoftLayer/managers/vs.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ def __init__(self, client, ordering_manager=None):
6161
def list_instances(self, hourly=True, monthly=True, tags=None, cpus=None,
6262
memory=None, hostname=None, domain=None,
6363
local_disk=None, datacenter=None, nic_speed=None,
64-
public_ip=None, private_ip=None, **kwargs):
64+
public_ip=None, private_ip=None, transient=None, **kwargs):
6565
"""Retrieve a list of all virtual servers on the account.
6666
6767
Example::
@@ -88,6 +88,7 @@ def list_instances(self, hourly=True, monthly=True, tags=None, cpus=None,
8888
:param integer nic_speed: filter based on network speed (in MBPS)
8989
:param string public_ip: filter based on public ip address
9090
:param string private_ip: filter based on private ip address
91+
:param boolean transient: filter on transient or non-transient instances
9192
:param dict \\*\\*kwargs: response-level options (mask, limit, etc.)
9293
:returns: Returns a list of dictionaries representing the matching
9394
virtual servers
@@ -157,6 +158,11 @@ def list_instances(self, hourly=True, monthly=True, tags=None, cpus=None,
157158
_filter['virtualGuests']['primaryBackendIpAddress'] = (
158159
utils.query_filter(private_ip))
159160

161+
if transient is not None:
162+
_filter['virtualGuests']['transientGuestFlag'] = (
163+
utils.query_filter(bool(transient))
164+
)
165+
160166
kwargs['filter'] = _filter.to_dict()
161167
kwargs['iter'] = True
162168
return self.client.call('Account', call, **kwargs)
@@ -194,6 +200,7 @@ def get_instance(self, instance_id, **kwargs):
194200
'provisionDate,'
195201
'notes,'
196202
'dedicatedAccountHostOnlyFlag,'
203+
'transientGuestFlag,'
197204
'privateNetworkOnlyFlag,'
198205
'primaryBackendIpAddress,'
199206
'primaryIpAddress,'
@@ -312,7 +319,7 @@ def _generate_create_dict(
312319
private_subnet=None, public_subnet=None,
313320
userdata=None, nic_speed=None, disks=None, post_uri=None,
314321
private=False, ssh_keys=None, public_security_groups=None,
315-
private_security_groups=None, boot_mode=None, **kwargs):
322+
private_security_groups=None, boot_mode=None, transient=False, **kwargs):
316323
"""Returns a dict appropriate to pass into Virtual_Guest::createObject
317324
318325
See :func:`create_instance` for a list of available options.
@@ -362,6 +369,9 @@ def _generate_create_dict(
362369
if private:
363370
data['privateNetworkOnlyFlag'] = private
364371

372+
if transient:
373+
data['transientGuestFlag'] = transient
374+
365375
if image_id:
366376
data["blockDeviceTemplateGroup"] = {"globalIdentifier": image_id}
367377
elif os_code:
@@ -505,6 +515,7 @@ def verify_create_instance(self, **kwargs):
505515
'flavor': 'BL1_1X2X100'
506516
'dedicated': False,
507517
'private': False,
518+
'transient': False,
508519
'os_code' : u'UBUNTU_LATEST',
509520
'hourly': True,
510521
'ssh_keys': [1234],
@@ -883,6 +894,7 @@ def order_guest(self, guest_object, test=False):
883894
'flavor': 'BL1_1X2X100'
884895
'dedicated': False,
885896
'private': False,
897+
'transient': False,
886898
'os_code' : u'UBUNTU_LATEST',
887899
'hourly': True,
888900
'ssh_keys': [1234],

tests/CLI/modules/vs/vs_create_tests.py

Lines changed: 74 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,48 @@ def test_create_like_flavor(self, confirm_mock):
485485
'networkComponents': [{'maxSpeed': 100}]},)
486486
self.assert_called_with('SoftLayer_Virtual_Guest', 'generateOrderTemplate', args=args)
487487

488+
@mock.patch('SoftLayer.CLI.formatting.confirm')
489+
def test_create_like_transient(self, confirm_mock):
490+
mock = self.set_mock('SoftLayer_Virtual_Guest', 'getObject')
491+
mock.return_value = {
492+
'hostname': 'vs-test-like',
493+
'domain': 'test.sftlyr.ws',
494+
'datacenter': {'name': 'dal05'},
495+
'networkComponents': [{'maxSpeed': 100}],
496+
'dedicatedAccountHostOnlyFlag': False,
497+
'privateNetworkOnlyFlag': False,
498+
'billingItem': {'orderItem': {'preset': {'keyName': 'B1_1X2X25'}}},
499+
'operatingSystem': {'softwareLicense': {
500+
'softwareDescription': {'referenceCode': 'UBUNTU_LATEST'}
501+
}},
502+
'hourlyBillingFlag': True,
503+
'localDiskFlag': False,
504+
'transientGuestFlag': True,
505+
'userData': {}
506+
}
507+
508+
confirm_mock.return_value = True
509+
result = self.run_command(['vs', 'create', '--like=123'])
510+
511+
self.assert_no_fail(result)
512+
self.assertIn('"guid": "1a2b3c-1701"', result.output)
513+
self.assert_called_with('SoftLayer_Product_Order', 'placeOrder')
514+
515+
args = ({'datacenter': {'name': 'dal05'},
516+
'domain': 'test.sftlyr.ws',
517+
'hourlyBillingFlag': True,
518+
'hostname': 'vs-test-like',
519+
'startCpus': None,
520+
'maxMemory': None,
521+
'localDiskFlag': None,
522+
'transientGuestFlag': True,
523+
'supplementalCreateObjectOptions': {
524+
'bootMode': None,
525+
'flavorKeyName': 'B1_1X2X25'},
526+
'operatingSystemReferenceCode': 'UBUNTU_LATEST',
527+
'networkComponents': [{'maxSpeed': 100}]},)
528+
self.assert_called_with('SoftLayer_Virtual_Guest', 'generateOrderTemplate', args=args)
529+
488530
@mock.patch('SoftLayer.CLI.formatting.confirm')
489531
def test_create_vs_test(self, confirm_mock):
490532
confirm_mock.return_value = True
@@ -511,9 +553,39 @@ def test_create_vs_bad_memory(self):
511553
result = self.run_command(['vs', 'create', '--hostname', 'TEST',
512554
'--domain', 'TESTING', '--cpu', '1',
513555
'--memory', '2034MB', '--flavor',
514-
'UBUNTU', '--datacenter', 'TEST00'])
556+
'B1_2X8X25', '--datacenter', 'TEST00'])
515557

516-
self.assertEqual(result.exit_code, 2)
558+
self.assertEqual(2, result.exit_code)
559+
560+
@mock.patch('SoftLayer.CLI.formatting.confirm')
561+
def test_create_vs_transient(self, confirm_mock):
562+
confirm_mock.return_value = True
563+
564+
result = self.run_command(['vs', 'create', '--hostname', 'TEST',
565+
'--domain', 'TESTING', '--flavor',
566+
'B1_2X8X25', '--datacenter', 'TEST00',
567+
'--transient', '--os', 'UBUNTU_LATEST'])
568+
569+
self.assert_no_fail(result)
570+
self.assertEqual(0, result.exit_code)
571+
572+
def test_create_vs_bad_transient_monthly(self):
573+
result = self.run_command(['vs', 'create', '--hostname', 'TEST',
574+
'--domain', 'TESTING', '--flavor',
575+
'B1_2X8X25', '--datacenter', 'TEST00',
576+
'--transient', '--billing', 'monthly',
577+
'--os', 'UBUNTU_LATEST'])
578+
579+
self.assertEqual(2, result.exit_code)
580+
581+
def test_create_vs_bad_transient_dedicated(self):
582+
result = self.run_command(['vs', 'create', '--hostname', 'TEST',
583+
'--domain', 'TESTING', '--flavor',
584+
'B1_2X8X25', '--datacenter', 'TEST00',
585+
'--transient', '--dedicated',
586+
'--os', 'UBUNTU_LATEST'])
587+
588+
self.assertEqual(2, result.exit_code)
517589

518590
@mock.patch('SoftLayer.CLI.formatting.confirm')
519591
def test_create_with_ipv6(self, confirm_mock):

tests/CLI/modules/vs/vs_tests.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -268,8 +268,7 @@ def test_create_options(self):
268268
result = self.run_command(['vs', 'create-options'])
269269

270270
self.assert_no_fail(result)
271-
self.assertEqual(json.loads(result.output),
272-
{'cpus (dedicated host)': [4, 56],
271+
self.assertEqual({'cpus (dedicated host)': [4, 56],
273272
'cpus (dedicated)': [1],
274273
'cpus (standard)': [1, 2, 3, 4],
275274
'datacenter': ['ams01', 'dal05'],
@@ -279,14 +278,16 @@ def test_create_options(self):
279278
'flavors (compute)': ['C1_1X2X25'],
280279
'flavors (memory)': ['M1_1X2X100'],
281280
'flavors (GPU)': ['AC1_1X2X100', 'ACL1_1X2X100'],
281+
'flavors (transient)': ['B1_1X2X25_TRANSIENT'],
282282
'local disk(0)': ['25', '100'],
283283
'memory': [1024, 2048, 3072, 4096],
284284
'memory (dedicated host)': [8192, 65536],
285285
'nic': ['10', '100', '1000'],
286286
'nic (dedicated host)': ['1000'],
287287
'os (CENTOS)': 'CENTOS_6_64',
288288
'os (DEBIAN)': 'DEBIAN_7_64',
289-
'os (UBUNTU)': 'UBUNTU_12_64'})
289+
'os (UBUNTU)': 'UBUNTU_12_64'},
290+
json.loads(result.output))
290291

291292
@mock.patch('SoftLayer.CLI.formatting.confirm')
292293
def test_dns_sync_both(self, confirm_mock):

0 commit comments

Comments
 (0)