Skip to content

Commit 817a9a5

Browse files
Merge pull request #995 from FernandoOjeda/fo_vlan_subnet
New Feature to create a vm in a specific subnet
2 parents aa3879e + 488979b commit 817a9a5

File tree

4 files changed

+242
-15
lines changed

4 files changed

+242
-15
lines changed

SoftLayer/CLI/virt/create.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,10 @@ def _parse_create_args(client, args):
133133
if args.get('vlan_private'):
134134
data['private_vlan'] = args['vlan_private']
135135

136+
data['public_subnet'] = args.get('subnet_public', None)
137+
138+
data['private_subnet'] = args.get('subnet_private', None)
139+
136140
if args.get('public_security_group'):
137141
pub_groups = args.get('public_security_group')
138142
data['public_security_groups'] = [group for group in pub_groups]
@@ -225,12 +229,18 @@ def _parse_create_args(client, args):
225229
type=click.Path(exists=True, readable=True, resolve_path=True))
226230
@click.option('--vlan-public',
227231
help="The ID of the public VLAN on which you want the virtual "
228-
"server placed",
232+
"server placed",
229233
type=click.INT)
230234
@click.option('--vlan-private',
231235
help="The ID of the private VLAN on which you want the virtual "
232236
"server placed",
233237
type=click.INT)
238+
@click.option('--subnet-public',
239+
help="The ID of the public SUBNET on which you want the virtual server placed",
240+
type=click.INT)
241+
@click.option('--subnet-private',
242+
help="The ID of the private SUBNET on which you want the virtual server placed",
243+
type=click.INT)
234244
@helpers.multi_option('--public-security-group',
235245
'-S',
236246
help=('Security group ID to associate with '

SoftLayer/managers/vs.py

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,9 @@
1616
from SoftLayer.managers import ordering
1717
from SoftLayer import utils
1818

19-
2019
LOGGER = logging.getLogger(__name__)
20+
21+
2122
# pylint: disable=no-self-use
2223

2324

@@ -305,6 +306,7 @@ def _generate_create_dict(
305306
hostname=None, domain=None, local_disk=True,
306307
datacenter=None, os_code=None, image_id=None,
307308
dedicated=False, public_vlan=None, private_vlan=None,
309+
private_subnet=None, public_subnet=None,
308310
userdata=None, nic_speed=None, disks=None, post_uri=None,
309311
private=False, ssh_keys=None, public_security_groups=None,
310312
private_security_groups=None, boot_mode=None, **kwargs):
@@ -365,14 +367,10 @@ def _generate_create_dict(
365367
if datacenter:
366368
data["datacenter"] = {"name": datacenter}
367369

368-
if public_vlan:
369-
data.update({
370-
'primaryNetworkComponent': {
371-
"networkVlan": {"id": int(public_vlan)}}})
372-
if private_vlan:
373-
data.update({
374-
"primaryBackendNetworkComponent": {
375-
"networkVlan": {"id": int(private_vlan)}}})
370+
if private_vlan or public_vlan or private_subnet or public_subnet:
371+
network_components = self._create_network_components(public_vlan, private_vlan,
372+
private_subnet, public_subnet)
373+
data.update(network_components)
376374

377375
if public_security_groups:
378376
secgroups = [{'securityGroup': {'id': int(sg)}}
@@ -415,6 +413,29 @@ def _generate_create_dict(
415413

416414
return data
417415

416+
def _create_network_components(
417+
self, public_vlan=None, private_vlan=None,
418+
private_subnet=None, public_subnet=None):
419+
420+
parameters = {}
421+
if private_vlan:
422+
parameters['primaryBackendNetworkComponent'] = {"networkVlan": {"id": int(private_vlan)}}
423+
if public_vlan:
424+
parameters['primaryNetworkComponent'] = {"networkVlan": {"id": int(public_vlan)}}
425+
if public_subnet:
426+
if public_vlan is None:
427+
raise exceptions.SoftLayerError("You need to specify a public_vlan with public_subnet")
428+
else:
429+
parameters['primaryNetworkComponent']['networkVlan']['primarySubnet'] = {'id': int(public_subnet)}
430+
if private_subnet:
431+
if private_vlan is None:
432+
raise exceptions.SoftLayerError("You need to specify a private_vlan with private_subnet")
433+
else:
434+
parameters['primaryBackendNetworkComponent']['networkVlan']['primarySubnet'] = {
435+
"id": int(private_subnet)}
436+
437+
return parameters
438+
418439
@retry(logger=LOGGER)
419440
def wait_for_transaction(self, instance_id, limit, delay=10):
420441
"""Waits on a VS transaction for the specified amount of time.

tests/CLI/modules/vs_tests.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,31 @@ def test_create(self, confirm_mock):
337337
self.assert_called_with('SoftLayer_Virtual_Guest', 'createObject',
338338
args=args)
339339

340+
@mock.patch('SoftLayer.CLI.formatting.confirm')
341+
def test_create_vlan_subnet(self, confirm_mock):
342+
confirm_mock.return_value = True
343+
344+
result = self.run_command(['vs', 'create',
345+
'--cpu=2',
346+
'--domain=example.com',
347+
'--hostname=host',
348+
'--os=UBUNTU_LATEST',
349+
'--memory=1',
350+
'--billing=hourly',
351+
'--datacenter=dal05',
352+
'--vlan-private=577940',
353+
'--subnet-private=478700',
354+
'--vlan-public=1639255',
355+
'--subnet-public=297614',
356+
'--tag=dev',
357+
'--tag=green'])
358+
359+
self.assert_no_fail(result)
360+
self.assertEqual(json.loads(result.output),
361+
{'guid': '1a2b3c-1701',
362+
'id': 100,
363+
'created': '2013-08-01 15:23:45'})
364+
340365
@mock.patch('SoftLayer.CLI.formatting.confirm')
341366
def test_create_with_wait_ready(self, confirm_mock):
342367
mock = self.set_mock('SoftLayer_Virtual_Guest', 'getObject')

tests/managers/vs_tests.py

Lines changed: 176 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,116 @@ def test_generate_public_vlan(self):
355355

356356
self.assertEqual(data, assert_data)
357357

358+
def test_generate_public_vlan_with_public_subnet(self):
359+
data = self.vs._generate_create_dict(
360+
cpus=1,
361+
memory=1,
362+
hostname='test',
363+
domain='example.com',
364+
os_code="STRING",
365+
public_vlan=1,
366+
public_subnet=1
367+
)
368+
369+
assert_data = {
370+
'startCpus': 1,
371+
'maxMemory': 1,
372+
'hostname': 'test',
373+
'domain': 'example.com',
374+
'localDiskFlag': True,
375+
'operatingSystemReferenceCode': "STRING",
376+
'hourlyBillingFlag': True,
377+
'primaryNetworkComponent': {'networkVlan': {'id': 1,
378+
'primarySubnet': {'id': 1}}},
379+
'supplementalCreateObjectOptions': {'bootMode': None},
380+
}
381+
382+
self.assertEqual(data, assert_data)
383+
384+
def test_generate_private_vlan_with_private_subnet(self):
385+
data = self.vs._generate_create_dict(
386+
cpus=1,
387+
memory=1,
388+
hostname='test',
389+
domain='example.com',
390+
os_code="STRING",
391+
private_vlan=1,
392+
private_subnet=1
393+
)
394+
395+
assert_data = {
396+
'startCpus': 1,
397+
'maxMemory': 1,
398+
'hostname': 'test',
399+
'domain': 'example.com',
400+
'localDiskFlag': True,
401+
'operatingSystemReferenceCode': "STRING",
402+
'hourlyBillingFlag': True,
403+
'primaryBackendNetworkComponent': {'networkVlan': {'id': 1,
404+
'primarySubnet': {'id': 1}}},
405+
'supplementalCreateObjectOptions': {'bootMode': None},
406+
}
407+
408+
self.assertEqual(data, assert_data)
409+
410+
def test_generate_private_vlan_subnet_public_vlan_subnet(self):
411+
data = self.vs._generate_create_dict(
412+
cpus=1,
413+
memory=1,
414+
hostname='test',
415+
domain='example.com',
416+
os_code="STRING",
417+
private_vlan=1,
418+
private_subnet=1,
419+
public_vlan=1,
420+
public_subnet=1,
421+
)
422+
423+
assert_data = {
424+
'startCpus': 1,
425+
'maxMemory': 1,
426+
'hostname': 'test',
427+
'domain': 'example.com',
428+
'localDiskFlag': True,
429+
'operatingSystemReferenceCode': "STRING",
430+
'hourlyBillingFlag': True,
431+
'primaryBackendNetworkComponent': {'networkVlan': {'id': 1,
432+
'primarySubnet': {'id': 1}}},
433+
'primaryNetworkComponent': {'networkVlan': {'id': 1,
434+
'primarySubnet': {'id': 1}}},
435+
'supplementalCreateObjectOptions': {'bootMode': None},
436+
}
437+
438+
self.assertEqual(data, assert_data)
439+
440+
def test_generate_private_subnet(self):
441+
actual = self.assertRaises(
442+
exceptions.SoftLayerError,
443+
self.vs._generate_create_dict,
444+
cpus=1,
445+
memory=1,
446+
hostname='test',
447+
domain='example.com',
448+
os_code="STRING",
449+
private_subnet=1,
450+
)
451+
452+
self.assertEqual(str(actual), "You need to specify a private_vlan with private_subnet")
453+
454+
def test_generate_public_subnet(self):
455+
actual = self.assertRaises(
456+
exceptions.SoftLayerError,
457+
self.vs._generate_create_dict,
458+
cpus=1,
459+
memory=1,
460+
hostname='test',
461+
domain='example.com',
462+
os_code="STRING",
463+
public_subnet=1,
464+
)
465+
466+
self.assertEqual(str(actual), "You need to specify a public_vlan with public_subnet")
467+
358468
def test_generate_private_vlan(self):
359469
data = self.vs._generate_create_dict(
360470
cpus=1,
@@ -373,12 +483,73 @@ def test_generate_private_vlan(self):
373483
'localDiskFlag': True,
374484
'operatingSystemReferenceCode': "STRING",
375485
'hourlyBillingFlag': True,
376-
'primaryBackendNetworkComponent': {"networkVlan": {"id": 1}},
486+
'primaryBackendNetworkComponent': {'networkVlan': {'id': 1}},
377487
'supplementalCreateObjectOptions': {'bootMode': None},
378488
}
379489

380490
self.assertEqual(data, assert_data)
381491

492+
def test_create_network_components_vlan_subnet_private_vlan_subnet_public(self):
493+
data = self.vs._create_network_components(
494+
private_vlan=1,
495+
private_subnet=1,
496+
public_vlan=1,
497+
public_subnet=1,
498+
)
499+
500+
assert_data = {
501+
'primaryBackendNetworkComponent': {'networkVlan': {'id': 1,
502+
'primarySubnet': {'id': 1}}},
503+
'primaryNetworkComponent': {'networkVlan': {'id': 1,
504+
'primarySubnet': {'id': 1}}},
505+
}
506+
507+
self.assertEqual(data, assert_data)
508+
509+
def test_create_network_components_vlan_subnet_private(self):
510+
data = self.vs._create_network_components(
511+
private_vlan=1,
512+
private_subnet=1,
513+
)
514+
515+
assert_data = {
516+
'primaryBackendNetworkComponent': {'networkVlan': {'id': 1,
517+
'primarySubnet': {'id': 1}}},
518+
}
519+
520+
self.assertEqual(data, assert_data)
521+
522+
def test_create_network_components_vlan_subnet_public(self):
523+
data = self.vs._create_network_components(
524+
public_vlan=1,
525+
public_subnet=1,
526+
)
527+
528+
assert_data = {
529+
'primaryNetworkComponent': {'networkVlan': {'id': 1,
530+
'primarySubnet': {'id': 1}}},
531+
}
532+
533+
self.assertEqual(data, assert_data)
534+
535+
def test_create_network_components_private_subnet(self):
536+
actual = self.assertRaises(
537+
exceptions.SoftLayerError,
538+
self.vs._create_network_components,
539+
private_subnet=1,
540+
)
541+
542+
self.assertEqual(str(actual), "You need to specify a private_vlan with private_subnet")
543+
544+
def test_create_network_components_public_subnet(self):
545+
actual = self.assertRaises(
546+
exceptions.SoftLayerError,
547+
self.vs._create_network_components,
548+
public_subnet=1,
549+
)
550+
551+
self.assertEqual(str(actual), "You need to specify a public_vlan with public_subnet")
552+
382553
def test_generate_userdata(self):
383554
data = self.vs._generate_create_dict(
384555
cpus=1,
@@ -621,10 +792,10 @@ def test_edit_full(self):
621792

622793
self.assertEqual(result, True)
623794
args = ({
624-
'hostname': 'new-host',
625-
'domain': 'new.sftlyr.ws',
626-
'notes': 'random notes',
627-
},)
795+
'hostname': 'new-host',
796+
'domain': 'new.sftlyr.ws',
797+
'notes': 'random notes',
798+
},)
628799
self.assert_called_with('SoftLayer_Virtual_Guest', 'editObject',
629800
identifier=100,
630801
args=args)

0 commit comments

Comments
 (0)