Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 70 additions & 8 deletions SoftLayer/CLI/dns/record_add.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,86 @@

import SoftLayer
from SoftLayer.CLI import environment
from SoftLayer.CLI import exceptions
from SoftLayer.CLI import helpers
# pylint: disable=redefined-builtin


@click.command()
@click.argument('zone')
@click.argument('record')
@click.argument('type')
@click.argument('record_type')
@click.argument('data')
@click.option('--zone',
help="Zone name or identifier that the resource record will be associated with.\n"
"Required for all record types except PTR")
@click.option('--ttl',
type=click.INT,
default=7200,
default=900,
show_default=True,
help='TTL value in seconds, such as 86400')
@click.option('--priority',
default=10,
show_default=True,
help='The priority of the target host. (MX or SRV type only)')
@click.option('--protocol',
type=click.Choice(['tcp', 'udp', 'tls']),
default='tcp',
show_default=True,
help='The protocol of the service, usually either TCP or UDP. (SRV type only)')
@click.option('--port',
type=click.INT,
help='The TCP/UDP/TLS port on which the service is to be found. (SRV type only)')
@click.option('--service',
help='The symbolic name of the desired service. (SRV type only)')
@click.option('--weight',
default=5,
show_default=True,
help='Relative weight for records with same priority. (SRV type only)')
@environment.pass_env
def cli(env, zone, record, type, data, ttl):
"""Add resource record."""
def cli(env, record, record_type, data, zone, ttl, priority, protocol, port, service, weight):
"""Add resource record.

Each resource record contains a RECORD and DATA property, defining a resource's name and it's target data.
Domains contain multiple types of resource records so it can take one of the following values: A, AAAA, CNAME,
MX, SPF, SRV, and PTR.

About reverse records (PTR), the RECORD value must to be the public Ip Address of device you would like to manage
reverse DNS.

slcli dns record-add 10.10.8.21 PTR myhost.com --ttl=900

Examples:

slcli dns record-add myhost.com A 192.168.1.10 --zone=foobar.com --ttl=900

slcli dns record-add myhost.com AAAA 2001:DB8::1 --zone=foobar.com

slcli dns record-add 192.168.1.2 MX 192.168.1.10 --zone=foobar.com --priority=11 --ttl=1800

slcli dns record-add myhost.com TXT "txt-verification=rXOxyZounZs87oacJSKvbUSIQ" --zone=2223334

slcli dns record-add myhost.com SPF "v=spf1 include:_spf.google.com ~all" --zone=2223334

slcli dns record-add myhost.com SRV 192.168.1.10 --zone=2223334 --service=foobar --port=80 --protocol=TCP

"""

manager = SoftLayer.DNSManager(env.client)
zone_id = helpers.resolve_id(manager.resolve_ids, zone, name='zone')
manager.create_record(zone_id, record, type, data, ttl=ttl)
record_type = record_type.upper()

if zone and record_type != 'PTR':
zone_id = helpers.resolve_id(manager.resolve_ids, zone, name='zone')

if record_type == 'MX':
manager.create_record_mx(zone_id, record, data, ttl=ttl, priority=priority)
elif record_type == 'SRV':
manager.create_record_srv(zone_id, record, data, protocol, port, service,
ttl=ttl, priority=priority, weight=weight)
else:
manager.create_record(zone_id, record, record_type, data, ttl=ttl)

elif record_type == 'PTR':
manager.create_record_ptr(record, data, ttl=ttl)
else:
raise exceptions.CLIAbort("%s isn't a valid record type or zone is missing" % record_type)

click.secho("%s record added successfully" % record_type, fg='green')
76 changes: 70 additions & 6 deletions SoftLayer/managers/dns.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,17 +89,81 @@ def create_record(self, zone_id, record, record_type, data, ttl=60):

:param integer id: the zone's ID
:param record: the name of the record to add
:param record_type: the type of record (A, AAAA, CNAME, MX, TXT, etc.)
:param record_type: the type of record (A, AAAA, CNAME, TXT, etc.)
:param data: the record's value
:param integer ttl: the TTL or time-to-live value (default: 60)

"""
return self.record.createObject({
'domainId': zone_id,
'ttl': ttl,
resource_record = self._generate_create_dict(record, record_type, data,
ttl, domainId=zone_id)
return self.record.createObject(resource_record)

def create_record_mx(self, zone_id, record, data, ttl=60, priority=10):
"""Create a mx resource record on a domain.

:param integer id: the zone's ID
:param record: the name of the record to add
:param data: the record's value
:param integer ttl: the TTL or time-to-live value (default: 60)
:param integer priority: the priority of the target host

"""
resource_record = self._generate_create_dict(record, 'MX', data, ttl,
domainId=zone_id, mxPriority=priority)
return self.record.createObject(resource_record)

def create_record_srv(self, zone_id, record, data, protocol, port, service,
ttl=60, priority=20, weight=10):
"""Create a resource record on a domain.

:param integer id: the zone's ID
:param record: the name of the record to add
:param data: the record's value
:param string protocol: the protocol of the service, usually either TCP or UDP.
:param integer port: the TCP or UDP port on which the service is to be found.
:param string service: the symbolic name of the desired service.
:param integer ttl: the TTL or time-to-live value (default: 60)
:param integer priority: the priority of the target host (default: 20)
:param integer weight: relative weight for records with same priority (default: 10)

"""
resource_record = self._generate_create_dict(record, 'SRV', data, ttl, domainId=zone_id,
priority=priority, protocol=protocol, port=port,
service=service, weight=weight)

# The createObject won't creates SRV records unless we send the following complexType.
resource_record['complexType'] = 'SoftLayer_Dns_Domain_ResourceRecord_SrvType'

return self.record.createObject(resource_record)

def create_record_ptr(self, record, data, ttl=60):
"""Create a reverse record.

:param record: the public ip address of device for which you would like to manage reverse DNS.
:param data: the record's value
:param integer ttl: the TTL or time-to-live value (default: 60)

"""
resource_record = self._generate_create_dict(record, 'PTR', data, ttl)

return self.record.createObject(resource_record)

@staticmethod
def _generate_create_dict(record, record_type, data, ttl, **kwargs):
"""Returns a dict appropriate to pass into Dns_Domain_ResourceRecord::createObject"""

# Basic dns record structure
resource_record = {
'host': record,
'type': record_type,
'data': data})
'data': data,
'ttl': ttl,
'type': record_type
}

for (key, value) in kwargs.items():
resource_record.setdefault(key, value)

return resource_record

def delete_record(self, record_id):
"""Delete a resource record by its ID.
Expand Down
36 changes: 33 additions & 3 deletions tests/CLI/modules/dns_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,41 @@ def test_list_records(self):
'ttl': 7200})

def test_add_record(self):
result = self.run_command(['dns', 'record-add', '1234', 'hostname',
'A', 'd', '--ttl=100'])
result = self.run_command(['dns', 'record-add', 'hostname', 'A',
'data', '--zone=1234', '--ttl=100'])

self.assert_no_fail(result)
self.assertEqual(result.output, "")
self.assertEqual(str(result.output), 'A record added successfully\n')

def test_add_record_mx(self):
result = self.run_command(['dns', 'record-add', 'hostname', 'MX',
'data', '--zone=1234', '--ttl=100', '--priority=25'])

self.assert_no_fail(result)
self.assertEqual(str(result.output), 'MX record added successfully\n')

def test_add_record_srv(self):
result = self.run_command(['dns', 'record-add', 'hostname', 'SRV',
'data', '--zone=1234', '--protocol=udp',
'--port=88', '--ttl=100', '--weight=5'])

self.assert_no_fail(result)
self.assertEqual(str(result.output), 'SRV record added successfully\n')

def test_add_record_ptr(self):
result = self.run_command(['dns', 'record-add', '192.168.1.1', 'PTR',
'hostname', '--ttl=100'])

self.assert_no_fail(result)
self.assertEqual(str(result.output), 'PTR record added successfully\n')

def test_add_record_abort(self):
result = self.run_command(['dns', 'record-add', 'hostname', 'A',
'data', '--ttl=100'])

self.assertEqual(result.exit_code, 2)
self.assertIsInstance(result.exception, exceptions.CLIAbort)
self.assertEqual(result.exception.message, "A isn't a valid record type or zone is missing")

@mock.patch('SoftLayer.CLI.formatting.no_going_back')
def test_delete_record(self, no_going_back_mock):
Expand Down
67 changes: 67 additions & 0 deletions tests/managers/dns_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,73 @@ def test_create_record(self):
},))
self.assertEqual(res, {'name': 'example.com'})

def test_create_record_mx(self):
res = self.dns_client.create_record_mx(1, 'test', 'testing', ttl=1200, priority=21)

self.assert_called_with('SoftLayer_Dns_Domain_ResourceRecord',
'createObject',
args=({
'domainId': 1,
'ttl': 1200,
'host': 'test',
'type': 'MX',
'data': 'testing',
'mxPriority': 21
},))
self.assertEqual(res, {'name': 'example.com'})

def test_create_record_srv(self):
res = self.dns_client.create_record_srv(1, 'record', 'test_data', 'SLS', 8080, 'foobar',
ttl=1200, priority=21, weight=15)

self.assert_called_with('SoftLayer_Dns_Domain_ResourceRecord',
'createObject',
args=({
'complexType': 'SoftLayer_Dns_Domain_ResourceRecord_SrvType',
'domainId': 1,
'ttl': 1200,
'host': 'record',
'type': 'SRV',
'data': 'test_data',
'priority': 21,
'weight': 15,
'service': 'foobar',
'port': 8080,
'protocol': 'SLS'
},))
self.assertEqual(res, {'name': 'example.com'})

def test_create_record_ptr(self):
res = self.dns_client.create_record_ptr('test', 'testing', ttl=1200)

self.assert_called_with('SoftLayer_Dns_Domain_ResourceRecord',
'createObject',
args=({
'ttl': 1200,
'host': 'test',
'type': 'PTR',
'data': 'testing'
},))
self.assertEqual(res, {'name': 'example.com'})

def test_generate_create_dict(self):
data = self.dns_client._generate_create_dict('foo', 'pmx', 'bar', 60, domainId=1234,
mxPriority=18, port=80, protocol='TCP', weight=25)

assert_data = {
'host': 'foo',
'data': 'bar',
'ttl': 60,
'type': 'pmx',
'domainId': 1234,
'mxPriority': 18,
'port': 80,
'protocol': 'TCP',
'weight': 25
}

self.assertEqual(data, assert_data)

def test_delete_record(self):
self.dns_client.delete_record(1)

Expand Down