diff --git a/SoftLayer/CLI/virt/create.py b/SoftLayer/CLI/virt/create.py index a3fa28b2c..0e721b464 100644 --- a/SoftLayer/CLI/virt/create.py +++ b/SoftLayer/CLI/virt/create.py @@ -25,8 +25,6 @@ def _update_with_like_args(ctx, _, value): like_args = { 'hostname': like_details['hostname'], 'domain': like_details['domain'], - 'cpu': like_details['maxCpu'], - 'memory': '%smb' % like_details['maxMemory'], 'hourly': like_details['hourlyBillingFlag'], 'datacenter': like_details['datacenter']['name'], 'network': like_details['networkComponents'][0]['maxSpeed'], @@ -36,6 +34,15 @@ def _update_with_like_args(ctx, _, value): 'private': like_details['privateNetworkOnlyFlag'], } + like_args['flavor'] = utils.lookup(like_details, + 'billingItem', + 'orderItem', + 'preset', + 'keyName') + if not like_args['flavor']: + like_args['cpu'] = like_details['maxCpu'] + like_args['memory'] = '%smb' % like_details['maxMemory'] + tag_refs = like_details.get('tagReferences', None) if tag_refs is not None and len(tag_refs) > 0: like_args['tag'] = [t['tag']['name'] for t in tag_refs] @@ -66,16 +73,22 @@ def _parse_create_args(client, args): """ data = { "hourly": args['billing'] == 'hourly', - "cpus": args['cpu'], "domain": args['domain'], "hostname": args['hostname'], "private": args['private'], "dedicated": args['dedicated'], "disks": args['disk'], - "local_disk": not args['san'], + "cpus": args.get('cpu', None), + "memory": args.get('memory', None), + "flavor": args.get('flavor', None) } - data["memory"] = args['memory'] + # The primary disk is included in the flavor and the local_disk flag is not needed + # Setting it to None prevents errors from the flag not matching the flavor + if not args.get('san') and args.get('flavor'): + data['local_disk'] = None + else: + data['local_disk'] = not args['san'] if args.get('os'): data['os_code'] = args['os'] @@ -130,6 +143,9 @@ def _parse_create_args(client, args): if args.get('tag'): data['tags'] = ','.join(args['tag']) + if args.get('host_id'): + data['host_id'] = args['host_id'] + return data @@ -143,15 +159,14 @@ def _parse_create_args(client, args): required=True, prompt=True) @click.option('--cpu', '-c', - help="Number of CPU cores", - type=click.INT, - required=True, - prompt=True) + help="Number of CPU cores (not available with flavors)", + type=click.INT) @click.option('--memory', '-m', - help="Memory in mebibytes", - type=virt.MEM_TYPE, - required=True, - prompt=True) + help="Memory in mebibytes (not available with flavors)", + type=virt.MEM_TYPE) +@click.option('--flavor', '-f', + help="Public Virtual Server flavor key name", + type=click.STRING) @click.option('--datacenter', '-d', help="Datacenter shortname", required=True, @@ -167,7 +182,10 @@ def _parse_create_args(client, args): help="Billing rate") @click.option('--dedicated/--public', is_flag=True, - help="Create a dedicated Virtual Server (Private Node)") + help="Create a Dedicated Virtual Server") +@click.option('--host-id', + type=click.INT, + help="Host Id to provision a Dedicated Host Virtual Server onto") @click.option('--san', is_flag=True, help="Use SAN storage instead of local disk.") @@ -305,6 +323,22 @@ def cli(env, **args): def _validate_args(env, args): """Raises an ArgumentError if the given arguments are not valid.""" + if all([args['cpu'], args['flavor']]): + raise exceptions.ArgumentError( + '[-c | --cpu] not allowed with [-f | --flavor]') + + if all([args['memory'], args['flavor']]): + raise exceptions.ArgumentError( + '[-m | --memory] not allowed with [-f | --flavor]') + + if all([args['dedicated'], args['flavor']]): + raise exceptions.ArgumentError( + '[-d | --dedicated] not allowed with [-f | --flavor]') + + if all([args['host_id'], args['flavor']]): + raise exceptions.ArgumentError( + '[-h | --host-id] not allowed with [-f | --flavor]') + if all([args['userdata'], args['userfile']]): raise exceptions.ArgumentError( '[-u | --userdata] not allowed with [-F | --userfile]') diff --git a/SoftLayer/CLI/virt/create_options.py b/SoftLayer/CLI/virt/create_options.py index 2aad92209..a4bfca45b 100644 --- a/SoftLayer/CLI/virt/create_options.py +++ b/SoftLayer/CLI/virt/create_options.py @@ -8,6 +8,7 @@ import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import formatting +from SoftLayer import utils @click.command() @@ -25,35 +26,63 @@ def cli(env): # Datacenters datacenters = [dc['template']['datacenter']['name'] for dc in result['datacenters']] + datacenters = sorted(datacenters) + table.add_row(['datacenter', formatting.listing(datacenters, separator='\n')]) - # CPUs - standard_cpu = [x for x in result['processors'] - if not x['template'].get( - 'dedicatedAccountHostOnlyFlag', False)] + def _add_flavor_rows(flavor_key, flavor_label, flavor_options): + flavors = [] + + for flavor_option in flavor_options: + flavor_key_name = utils.lookup(flavor_option, 'flavor', 'keyName') + if not flavor_key_name.startswith(flavor_key): + continue - ded_cpu = [x for x in result['processors'] - if x['template'].get('dedicatedAccountHostOnlyFlag', - False)] + flavors.append(flavor_key_name) - def add_cpus_row(cpu_options, name): - """Add CPU rows to the table.""" - cpus = [] - for cpu_option in cpu_options: - cpus.append(str(cpu_option['template']['startCpus'])) + if len(flavors) > 0: + table.add_row(['flavors (%s)' % flavor_label, + formatting.listing(flavors, separator='\n')]) - table.add_row(['cpus (%s)' % name, - formatting.listing(cpus, separator=',')]) + if result.get('flavors', None): + _add_flavor_rows('B1', 'balanced', result['flavors']) + _add_flavor_rows('BL1', 'balanced local - hdd', result['flavors']) + _add_flavor_rows('BL2', 'balanced local - ssd', result['flavors']) + _add_flavor_rows('C1', 'compute', result['flavors']) + _add_flavor_rows('M1', 'memory', result['flavors']) - add_cpus_row(ded_cpu, 'private') - add_cpus_row(standard_cpu, 'standard') + # CPUs + standard_cpus = [int(x['template']['startCpus']) for x in result['processors'] + if not x['template'].get('dedicatedAccountHostOnlyFlag', + False) + and not x['template'].get('dedicatedHost', None)] + ded_cpus = [int(x['template']['startCpus']) for x in result['processors'] + if x['template'].get('dedicatedAccountHostOnlyFlag', False)] + ded_host_cpus = [int(x['template']['startCpus']) for x in result['processors'] + if x['template'].get('dedicatedHost', None)] + + standard_cpus = sorted(standard_cpus) + table.add_row(['cpus (standard)', formatting.listing(standard_cpus, separator=',')]) + ded_cpus = sorted(ded_cpus) + table.add_row(['cpus (dedicated)', formatting.listing(ded_cpus, separator=',')]) + ded_host_cpus = sorted(ded_host_cpus) + table.add_row(['cpus (dedicated host)', formatting.listing(ded_host_cpus, separator=',')]) # Memory - memory = [str(m['template']['maxMemory']) for m in result['memory']] + memory = [int(m['template']['maxMemory']) for m in result['memory'] + if not m['itemPrice'].get('dedicatedHostInstanceFlag', False)] + ded_host_memory = [int(m['template']['maxMemory']) for m in result['memory'] + if m['itemPrice'].get('dedicatedHostInstanceFlag', False)] + + memory = sorted(memory) table.add_row(['memory', formatting.listing(memory, separator=',')]) + ded_host_memory = sorted(ded_host_memory) + table.add_row(['memory (dedicated host)', + formatting.listing(ded_host_memory, separator=',')]) + # Operating Systems op_sys = [o['template']['operatingSystemReferenceCode'] for o in result['operatingSystems']] @@ -73,7 +102,14 @@ def add_cpus_row(cpu_options, name): # Disk local_disks = [x for x in result['blockDevices'] - if x['template'].get('localDiskFlag', False)] + if x['template'].get('localDiskFlag', False) + and not x['itemPrice'].get('dedicatedHostInstanceFlag', + False)] + + ded_host_local_disks = [x for x in result['blockDevices'] + if x['template'].get('localDiskFlag', False) + and x['itemPrice'].get('dedicatedHostInstanceFlag', + False)] san_disks = [x for x in result['blockDevices'] if not x['template'].get('localDiskFlag', False)] @@ -95,17 +131,37 @@ def add_block_rows(disks, name): formatting.listing(simple[label], separator=',')]) - add_block_rows(local_disks, 'local') add_block_rows(san_disks, 'san') + add_block_rows(local_disks, 'local') + add_block_rows(ded_host_local_disks, 'local (dedicated host)') # Network speeds = [] - for comp in result['networkComponents']: - speed = comp['template']['networkComponents'][0]['maxSpeed'] - speeds.append(str(speed)) + ded_host_speeds = [] + for option in result['networkComponents']: + template = option.get('template', None) + price = option.get('itemPrice', None) + + if not template or not price \ + or not template.get('networkComponents', None): + continue + + if not template['networkComponents'][0] \ + or not template['networkComponents'][0].get('maxSpeed', None): + continue + + max_speed = str(template['networkComponents'][0]['maxSpeed']) + if price.get('dedicatedHostInstanceFlag', False) \ + and max_speed not in ded_host_speeds: + ded_host_speeds.append(max_speed) + elif max_speed not in speeds: + speeds.append(max_speed) speeds = sorted(speeds) - table.add_row(['nic', formatting.listing(speeds, separator=',')]) + ded_host_speeds = sorted(ded_host_speeds) + table.add_row(['nic (dedicated host)', + formatting.listing(ded_host_speeds, separator=',')]) + env.fout(table) diff --git a/SoftLayer/fixtures/SoftLayer_Virtual_Guest.py b/SoftLayer/fixtures/SoftLayer_Virtual_Guest.py index 6f284950b..2a8cd8c9e 100644 --- a/SoftLayer/fixtures/SoftLayer_Virtual_Guest.py +++ b/SoftLayer/fixtures/SoftLayer_Virtual_Guest.py @@ -30,10 +30,10 @@ 'primaryIpAddress': '172.16.240.2', 'globalIdentifier': '1a2b3c-1701', 'primaryBackendIpAddress': '10.45.19.37', - "primaryNetworkComponent": {"speed": 10, "maxSpeed": 100}, + 'primaryNetworkComponent': {'speed': 10, 'maxSpeed': 100}, 'hourlyBillingFlag': False, 'createDate': '2013-08-01 15:23:45', - 'blockDevices': [{'device': 0, 'mountType': 'Disk', "uuid": 1}, + 'blockDevices': [{'device': 0, 'mountType': 'Disk', 'uuid': 1}, {'device': 1, 'mountType': 'Disk', 'diskImage': {'type': {'keyName': 'SWAP'}}}, {'device': 2, 'mountType': 'CD'}, @@ -59,6 +59,68 @@ } getCreateObjectOptions = { + 'flavors': [ + { + 'flavor': { + 'keyName': 'B1_1X2X25' + }, + 'template': { + 'supplementalCreateObjectOptions': { + 'flavorKeyName': 'B1_1X2X25' + } + } + }, + { + 'flavor': { + 'keyName': 'B1_1X2X100' + }, + 'template': { + 'supplementalCreateObjectOptions': { + 'flavorKeyName': 'B1_1X2X100' + } + } + }, + { + 'flavor': { + 'keyName': 'BL1_1X2X100' + }, + 'template': { + 'supplementalCreateObjectOptions': { + 'flavorKeyName': 'BL1_1X2X100' + } + } + }, + { + 'flavor': { + 'keyName': 'BL2_1X2X100' + }, + 'template': { + 'supplementalCreateObjectOptions': { + 'flavorKeyName': 'BL2_1X2X100' + } + } + }, + { + 'flavor': { + 'keyName': 'C1_1X2X25' + }, + 'template': { + 'supplementalCreateObjectOptions': { + 'flavorKeyName': 'C1_1X2X25' + } + } + }, + { + 'flavor': { + 'keyName': 'M1_1X2X100' + }, + 'template': { + 'supplementalCreateObjectOptions': { + 'flavorKeyName': 'M1_1X2X100' + } + } + }, + ], 'processors': [ { 'itemPrice': { @@ -92,6 +154,52 @@ }, 'template': {'startCpus': 4} }, + { + 'itemPrice': { + 'hourlyRecurringFee': '.209', + 'recurringFee': '139', + 'dedicatedHostInstanceFlag': False, + 'item': { + 'description': '1 x 2.0 GHz Cores (Dedicated)' + } + }, + 'template': { + 'dedicatedAccountHostOnlyFlag': True, + 'startCpus': 1 + } + }, + { + 'itemPrice': { + 'hourlyRecurringFee': '0', + 'recurringFee': '0', + 'dedicatedHostInstanceFlag': True, + 'item': { + 'description': '56 x 2.0 GHz Cores (Dedicated Host)' + } + }, + 'template': { + 'startCpus': 56, + 'dedicatedHost': { + 'id': None + } + } + }, + { + 'itemPrice': { + 'hourlyRecurringFee': '0', + 'recurringFee': '0', + 'dedicatedHostInstanceFlag': True, + 'item': { + 'description': '4 x 2.0 GHz Cores (Dedicated Host)' + } + }, + 'template': { + 'startCpus': 4, + 'dedicatedHost': { + 'id': None + } + } + }, ], 'memory': [ { @@ -125,6 +233,32 @@ }, 'template': {'maxMemory': 4096} }, + { + 'itemPrice': { + 'hourlyRecurringFee': '0', + 'recurringFee': '0', + 'dedicatedHostInstanceFlag': True, + 'item': { + 'description': '64 GB (Dedicated Host)' + } + }, + 'template': { + 'maxMemory': 65536 + } + }, + { + 'itemPrice': { + 'hourlyRecurringFee': '0', + 'recurringFee': '0', + 'dedicatedHostInstanceFlag': True, + 'item': { + 'description': '8 GB (Dedicated Host)' + } + }, + 'template': { + 'maxMemory': 8192 + } + }, ], 'blockDevices': [ { @@ -223,7 +357,25 @@ 'template': { 'networkComponents': [{'maxSpeed': 1000}] } - } + }, + { + 'itemPrice': { + 'hourlyRecurringFee': '0', + 'recurringFee': '0', + 'dedicatedHostInstanceFlag': True, + 'item': { + 'description': '1 Gbps Public & Private Network Uplinks (Dedicated Host)' + } + }, + 'template': { + 'networkComponents': [ + { + 'maxSpeed': 1000 + } + ], + 'privateNetworkOnlyFlag': False + } + }, ], 'datacenters': [ {'template': {'datacenter': {'name': 'ams01'}}}, diff --git a/SoftLayer/managers/vs.py b/SoftLayer/managers/vs.py index 528043603..afb441c33 100644 --- a/SoftLayer/managers/vs.py +++ b/SoftLayer/managers/vs.py @@ -217,13 +217,14 @@ def get_instance(self, instance_id, **kwargs): referenceCode]]],''' 'hourlyBillingFlag,' 'userData,' - 'billingItem[' - 'id,nextInvoiceTotalRecurringAmount,' - 'children[categoryCode,nextInvoiceTotalRecurringAmount],' - 'orderItem.order.userRecord[username]' - '],' + '''billingItem[id,nextInvoiceTotalRecurringAmount, + children[categoryCode,nextInvoiceTotalRecurringAmount], + orderItem[id, + order.userRecord[username], + preset.keyName]],''' 'tagReferences[id,tag[name,id]],' - 'networkVlans[id,vlanNumber,networkSpace]' + 'networkVlans[id,vlanNumber,networkSpace],' + 'dedicatedHost.id' ) return self.guest.getObject(id=instance_id, **kwargs) @@ -299,19 +300,26 @@ def _generate_create_dict( dedicated=False, public_vlan=None, private_vlan=None, userdata=None, nic_speed=None, disks=None, post_uri=None, private=False, ssh_keys=None, public_security_groups=None, - private_security_groups=None): + private_security_groups=None, **kwargs): """Returns a dict appropriate to pass into Virtual_Guest::createObject See :func:`create_instance` for a list of available options. """ - required = [cpus, memory, hostname, domain] + required = [hostname, domain] + + flavor = kwargs.get('flavor', None) + host_id = kwargs.get('host_id', None) mutually_exclusive = [ - {'os_code': os_code, "image_id": image_id}, + {'os_code': os_code, 'image_id': image_id}, + {'cpu': cpus, 'flavor': flavor}, + {'memory': memory, 'flavor': flavor}, + {'flavor': flavor, 'dedicated': dedicated}, + {'flavor': flavor, 'host_id': host_id} ] if not all(required): - raise ValueError("cpu, memory, hostname, and domain are required") + raise ValueError("hostname, and domain are required") for mu_ex in mutually_exclusive: if all(mu_ex.values()): @@ -319,18 +327,23 @@ def _generate_create_dict( 'Can only specify one of: %s' % (','.join(mu_ex.keys()))) data = { - "startCpus": int(cpus), - "maxMemory": int(memory), + "startCpus": cpus, + "maxMemory": memory, "hostname": hostname, "domain": domain, "localDiskFlag": local_disk, + "hourlyBillingFlag": hourly } - data["hourlyBillingFlag"] = hourly + if flavor: + data["supplementalCreateObjectOptions"] = {"flavorKeyName": flavor} - if dedicated: + if dedicated and not host_id: data["dedicatedAccountHostOnlyFlag"] = dedicated + if host_id: + data["dedicatedHost"] = {"id": host_id} + if private: data['privateNetworkOnlyFlag'] = private @@ -562,6 +575,10 @@ def create_instance(self, **kwargs): :param list ssh_keys: The SSH keys to add to the root user :param int nic_speed: The port speed to set :param string tags: tags to set on the VS as a comma separated list + :param string flavor: The key name of the public virtual server flavor + being ordered. + :param int host_id: The host id of a dedicated host to provision a + dedicated host virtual server on. """ tags = kwargs.pop('tags', None) inst = self.guest.createObject(self._generate_create_dict(**kwargs)) diff --git a/tests/CLI/modules/vs_tests.py b/tests/CLI/modules/vs_tests.py index 88269f20b..cc98ee3f3 100644 --- a/tests/CLI/modules/vs_tests.py +++ b/tests/CLI/modules/vs_tests.py @@ -93,12 +93,20 @@ def test_create_options(self): self.assert_no_fail(result) self.assertEqual(json.loads(result.output), - {'cpus (private)': [], - 'cpus (standard)': ['1', '2', '3', '4'], + {'cpus (dedicated host)': [4, 56], + 'cpus (dedicated)': [1], + 'cpus (standard)': [1, 2, 3, 4], 'datacenter': ['ams01', 'dal05'], + 'flavors (balanced)': ['B1_1X2X25', 'B1_1X2X100'], + 'flavors (balanced local - hdd)': ['BL1_1X2X100'], + 'flavors (balanced local - ssd)': ['BL2_1X2X100'], + 'flavors (compute)': ['C1_1X2X25'], + 'flavors (memory)': ['M1_1X2X100'], 'local disk(0)': ['25', '100'], - 'memory': ['1024', '2048', '3072', '4096'], + 'memory': [1024, 2048, 3072, 4096], + 'memory (dedicated host)': [8192, 65536], 'nic': ['10', '100', '1000'], + 'nic (dedicated host)': ['1000'], 'os (CENTOS)': 'CENTOS_6_64', 'os (DEBIAN)': 'DEBIAN_7_64', 'os (UBUNTU)': 'UBUNTU_12_64'}) @@ -171,6 +179,159 @@ def test_create_with_integer_image_id(self, confirm_mock): self.assert_called_with('SoftLayer_Virtual_Guest', 'createObject', args=args) + @mock.patch('SoftLayer.CLI.formatting.confirm') + def test_create_with_flavor(self, confirm_mock): + confirm_mock.return_value = True + result = self.run_command(['vs', 'create', + '--domain=example.com', + '--hostname=host', + '--os=UBUNTU_LATEST', + '--network=100', + '--billing=hourly', + '--datacenter=dal05', + '--flavor=B1_1X2X25']) + + self.assert_no_fail(result) + self.assertEqual(json.loads(result.output), + {'guid': '1a2b3c-1701', + 'id': 100, + 'created': '2013-08-01 15:23:45'}) + + args = ({'datacenter': {'name': 'dal05'}, + 'domain': 'example.com', + 'hourlyBillingFlag': True, + 'hostname': 'host', + 'startCpus': None, + 'maxMemory': None, + 'localDiskFlag': None, + 'supplementalCreateObjectOptions': {'flavorKeyName': 'B1_1X2X25'}, + 'operatingSystemReferenceCode': 'UBUNTU_LATEST', + 'networkComponents': [{'maxSpeed': '100'}]},) + self.assert_called_with('SoftLayer_Virtual_Guest', 'createObject', + args=args) + + @mock.patch('SoftLayer.CLI.formatting.confirm') + def test_create_with_host_id(self, confirm_mock): + confirm_mock.return_value = True + result = self.run_command(['vs', 'create', + '--cpu=2', + '--domain=example.com', + '--hostname=host', + '--os=UBUNTU_LATEST', + '--memory=1', + '--network=100', + '--billing=hourly', + '--datacenter=dal05', + '--dedicated', + '--host-id=123']) + + self.assert_no_fail(result) + self.assertEqual(json.loads(result.output), + {'guid': '1a2b3c-1701', + 'id': 100, + 'created': '2013-08-01 15:23:45'}) + + args = ({'datacenter': {'name': 'dal05'}, + 'domain': 'example.com', + 'hourlyBillingFlag': True, + 'localDiskFlag': True, + 'maxMemory': 1024, + 'hostname': 'host', + 'startCpus': 2, + 'operatingSystemReferenceCode': 'UBUNTU_LATEST', + 'networkComponents': [{'maxSpeed': '100'}], + 'dedicatedHost': {'id': 123}},) + self.assert_called_with('SoftLayer_Virtual_Guest', 'createObject', + args=args) + + @mock.patch('SoftLayer.CLI.formatting.confirm') + def test_create_like(self, confirm_mock): + mock = self.set_mock('SoftLayer_Virtual_Guest', 'getObject') + mock.return_value = { + 'hostname': 'vs-test-like', + 'domain': 'test.sftlyr.ws', + 'maxCpu': 2, + 'maxMemory': 1024, + 'datacenter': {'name': 'dal05'}, + 'networkComponents': [{'maxSpeed': 100}], + 'dedicatedAccountHostOnlyFlag': False, + 'privateNetworkOnlyFlag': False, + 'billingItem': {'orderItem': {'preset': {}}}, + 'operatingSystem': {'softwareLicense': { + 'softwareDescription': {'referenceCode': 'UBUNTU_LATEST'} + }}, + 'hourlyBillingFlag': False, + 'localDiskFlag': True, + 'userData': {} + } + + confirm_mock.return_value = True + result = self.run_command(['vs', 'create', + '--like=123', + '--san', + '--billing=hourly']) + + self.assert_no_fail(result) + self.assertEqual(json.loads(result.output), + {'guid': '1a2b3c-1701', + 'id': 100, + 'created': '2013-08-01 15:23:45'}) + + args = ({'datacenter': {'name': 'dal05'}, + 'domain': 'test.sftlyr.ws', + 'hourlyBillingFlag': True, + 'hostname': 'vs-test-like', + 'startCpus': 2, + 'maxMemory': 1024, + 'localDiskFlag': False, + 'operatingSystemReferenceCode': 'UBUNTU_LATEST', + 'networkComponents': [{'maxSpeed': 100}]},) + self.assert_called_with('SoftLayer_Virtual_Guest', 'createObject', + args=args) + + @mock.patch('SoftLayer.CLI.formatting.confirm') + def test_create_like_flavor(self, confirm_mock): + mock = self.set_mock('SoftLayer_Virtual_Guest', 'getObject') + mock.return_value = { + 'hostname': 'vs-test-like', + 'domain': 'test.sftlyr.ws', + 'maxCpu': 2, + 'maxMemory': 1024, + 'datacenter': {'name': 'dal05'}, + 'networkComponents': [{'maxSpeed': 100}], + 'dedicatedAccountHostOnlyFlag': False, + 'privateNetworkOnlyFlag': False, + 'billingItem': {'orderItem': {'preset': {'keyName': 'B1_1X2X25'}}}, + 'operatingSystem': {'softwareLicense': { + 'softwareDescription': {'referenceCode': 'UBUNTU_LATEST'} + }}, + 'hourlyBillingFlag': True, + 'localDiskFlag': False, + 'userData': {} + } + + confirm_mock.return_value = True + result = self.run_command(['vs', 'create', '--like=123']) + + self.assert_no_fail(result) + self.assertEqual(json.loads(result.output), + {'guid': '1a2b3c-1701', + 'id': 100, + 'created': '2013-08-01 15:23:45'}) + + args = ({'datacenter': {'name': 'dal05'}, + 'domain': 'test.sftlyr.ws', + 'hourlyBillingFlag': True, + 'hostname': 'vs-test-like', + 'startCpus': None, + 'maxMemory': None, + 'localDiskFlag': None, + 'supplementalCreateObjectOptions': {'flavorKeyName': 'B1_1X2X25'}, + 'operatingSystemReferenceCode': 'UBUNTU_LATEST', + 'networkComponents': [{'maxSpeed': 100}]},) + self.assert_called_with('SoftLayer_Virtual_Guest', 'createObject', + args=args) + @mock.patch('SoftLayer.CLI.formatting.confirm') def test_dns_sync_both(self, confirm_mock): confirm_mock.return_value = True