Skip to content

Commit 1a58b24

Browse files
Merge pull request #1037 from acamacho82/Issue-584
New methods in DNS manager. Allows to create MX, SRV and PTR records.
2 parents 7c1a3ea + 6fe950c commit 1a58b24

File tree

4 files changed

+240
-17
lines changed

4 files changed

+240
-17
lines changed

SoftLayer/CLI/dns/record_add.py

Lines changed: 70 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,86 @@
55

66
import SoftLayer
77
from SoftLayer.CLI import environment
8+
from SoftLayer.CLI import exceptions
89
from SoftLayer.CLI import helpers
910
# pylint: disable=redefined-builtin
1011

1112

1213
@click.command()
13-
@click.argument('zone')
1414
@click.argument('record')
15-
@click.argument('type')
15+
@click.argument('record_type')
1616
@click.argument('data')
17+
@click.option('--zone',
18+
help="Zone name or identifier that the resource record will be associated with.\n"
19+
"Required for all record types except PTR")
1720
@click.option('--ttl',
18-
type=click.INT,
19-
default=7200,
21+
default=900,
2022
show_default=True,
2123
help='TTL value in seconds, such as 86400')
24+
@click.option('--priority',
25+
default=10,
26+
show_default=True,
27+
help='The priority of the target host. (MX or SRV type only)')
28+
@click.option('--protocol',
29+
type=click.Choice(['tcp', 'udp', 'tls']),
30+
default='tcp',
31+
show_default=True,
32+
help='The protocol of the service, usually either TCP or UDP. (SRV type only)')
33+
@click.option('--port',
34+
type=click.INT,
35+
help='The TCP/UDP/TLS port on which the service is to be found. (SRV type only)')
36+
@click.option('--service',
37+
help='The symbolic name of the desired service. (SRV type only)')
38+
@click.option('--weight',
39+
default=5,
40+
show_default=True,
41+
help='Relative weight for records with same priority. (SRV type only)')
2242
@environment.pass_env
23-
def cli(env, zone, record, type, data, ttl):
24-
"""Add resource record."""
43+
def cli(env, record, record_type, data, zone, ttl, priority, protocol, port, service, weight):
44+
"""Add resource record.
45+
46+
Each resource record contains a RECORD and DATA property, defining a resource's name and it's target data.
47+
Domains contain multiple types of resource records so it can take one of the following values: A, AAAA, CNAME,
48+
MX, SPF, SRV, and PTR.
49+
50+
About reverse records (PTR), the RECORD value must to be the public Ip Address of device you would like to manage
51+
reverse DNS.
52+
53+
slcli dns record-add 10.10.8.21 PTR myhost.com --ttl=900
54+
55+
Examples:
56+
57+
slcli dns record-add myhost.com A 192.168.1.10 --zone=foobar.com --ttl=900
58+
59+
slcli dns record-add myhost.com AAAA 2001:DB8::1 --zone=foobar.com
60+
61+
slcli dns record-add 192.168.1.2 MX 192.168.1.10 --zone=foobar.com --priority=11 --ttl=1800
62+
63+
slcli dns record-add myhost.com TXT "txt-verification=rXOxyZounZs87oacJSKvbUSIQ" --zone=2223334
64+
65+
slcli dns record-add myhost.com SPF "v=spf1 include:_spf.google.com ~all" --zone=2223334
66+
67+
slcli dns record-add myhost.com SRV 192.168.1.10 --zone=2223334 --service=foobar --port=80 --protocol=TCP
68+
69+
"""
2570

2671
manager = SoftLayer.DNSManager(env.client)
27-
zone_id = helpers.resolve_id(manager.resolve_ids, zone, name='zone')
28-
manager.create_record(zone_id, record, type, data, ttl=ttl)
72+
record_type = record_type.upper()
73+
74+
if zone and record_type != 'PTR':
75+
zone_id = helpers.resolve_id(manager.resolve_ids, zone, name='zone')
76+
77+
if record_type == 'MX':
78+
manager.create_record_mx(zone_id, record, data, ttl=ttl, priority=priority)
79+
elif record_type == 'SRV':
80+
manager.create_record_srv(zone_id, record, data, protocol, port, service,
81+
ttl=ttl, priority=priority, weight=weight)
82+
else:
83+
manager.create_record(zone_id, record, record_type, data, ttl=ttl)
84+
85+
elif record_type == 'PTR':
86+
manager.create_record_ptr(record, data, ttl=ttl)
87+
else:
88+
raise exceptions.CLIAbort("%s isn't a valid record type or zone is missing" % record_type)
89+
90+
click.secho("%s record added successfully" % record_type, fg='green')

SoftLayer/managers/dns.py

Lines changed: 70 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -89,17 +89,81 @@ def create_record(self, zone_id, record, record_type, data, ttl=60):
8989
9090
:param integer id: the zone's ID
9191
:param record: the name of the record to add
92-
:param record_type: the type of record (A, AAAA, CNAME, MX, TXT, etc.)
92+
:param record_type: the type of record (A, AAAA, CNAME, TXT, etc.)
9393
:param data: the record's value
9494
:param integer ttl: the TTL or time-to-live value (default: 60)
9595
9696
"""
97-
return self.record.createObject({
98-
'domainId': zone_id,
99-
'ttl': ttl,
97+
resource_record = self._generate_create_dict(record, record_type, data,
98+
ttl, domainId=zone_id)
99+
return self.record.createObject(resource_record)
100+
101+
def create_record_mx(self, zone_id, record, data, ttl=60, priority=10):
102+
"""Create a mx resource record on a domain.
103+
104+
:param integer id: the zone's ID
105+
:param record: the name of the record to add
106+
:param data: the record's value
107+
:param integer ttl: the TTL or time-to-live value (default: 60)
108+
:param integer priority: the priority of the target host
109+
110+
"""
111+
resource_record = self._generate_create_dict(record, 'MX', data, ttl,
112+
domainId=zone_id, mxPriority=priority)
113+
return self.record.createObject(resource_record)
114+
115+
def create_record_srv(self, zone_id, record, data, protocol, port, service,
116+
ttl=60, priority=20, weight=10):
117+
"""Create a resource record on a domain.
118+
119+
:param integer id: the zone's ID
120+
:param record: the name of the record to add
121+
:param data: the record's value
122+
:param string protocol: the protocol of the service, usually either TCP or UDP.
123+
:param integer port: the TCP or UDP port on which the service is to be found.
124+
:param string service: the symbolic name of the desired service.
125+
:param integer ttl: the TTL or time-to-live value (default: 60)
126+
:param integer priority: the priority of the target host (default: 20)
127+
:param integer weight: relative weight for records with same priority (default: 10)
128+
129+
"""
130+
resource_record = self._generate_create_dict(record, 'SRV', data, ttl, domainId=zone_id,
131+
priority=priority, protocol=protocol, port=port,
132+
service=service, weight=weight)
133+
134+
# The createObject won't creates SRV records unless we send the following complexType.
135+
resource_record['complexType'] = 'SoftLayer_Dns_Domain_ResourceRecord_SrvType'
136+
137+
return self.record.createObject(resource_record)
138+
139+
def create_record_ptr(self, record, data, ttl=60):
140+
"""Create a reverse record.
141+
142+
:param record: the public ip address of device for which you would like to manage reverse DNS.
143+
:param data: the record's value
144+
:param integer ttl: the TTL or time-to-live value (default: 60)
145+
146+
"""
147+
resource_record = self._generate_create_dict(record, 'PTR', data, ttl)
148+
149+
return self.record.createObject(resource_record)
150+
151+
@staticmethod
152+
def _generate_create_dict(record, record_type, data, ttl, **kwargs):
153+
"""Returns a dict appropriate to pass into Dns_Domain_ResourceRecord::createObject"""
154+
155+
# Basic dns record structure
156+
resource_record = {
100157
'host': record,
101-
'type': record_type,
102-
'data': data})
158+
'data': data,
159+
'ttl': ttl,
160+
'type': record_type
161+
}
162+
163+
for (key, value) in kwargs.items():
164+
resource_record.setdefault(key, value)
165+
166+
return resource_record
103167

104168
def delete_record(self, record_id):
105169
"""Delete a resource record by its ID.

tests/CLI/modules/dns_tests.py

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,11 +72,41 @@ def test_list_records(self):
7272
'ttl': 7200})
7373

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

7878
self.assert_no_fail(result)
79-
self.assertEqual(result.output, "")
79+
self.assertEqual(str(result.output), 'A record added successfully\n')
80+
81+
def test_add_record_mx(self):
82+
result = self.run_command(['dns', 'record-add', 'hostname', 'MX',
83+
'data', '--zone=1234', '--ttl=100', '--priority=25'])
84+
85+
self.assert_no_fail(result)
86+
self.assertEqual(str(result.output), 'MX record added successfully\n')
87+
88+
def test_add_record_srv(self):
89+
result = self.run_command(['dns', 'record-add', 'hostname', 'SRV',
90+
'data', '--zone=1234', '--protocol=udp',
91+
'--port=88', '--ttl=100', '--weight=5'])
92+
93+
self.assert_no_fail(result)
94+
self.assertEqual(str(result.output), 'SRV record added successfully\n')
95+
96+
def test_add_record_ptr(self):
97+
result = self.run_command(['dns', 'record-add', '192.168.1.1', 'PTR',
98+
'hostname', '--ttl=100'])
99+
100+
self.assert_no_fail(result)
101+
self.assertEqual(str(result.output), 'PTR record added successfully\n')
102+
103+
def test_add_record_abort(self):
104+
result = self.run_command(['dns', 'record-add', 'hostname', 'A',
105+
'data', '--ttl=100'])
106+
107+
self.assertEqual(result.exit_code, 2)
108+
self.assertIsInstance(result.exception, exceptions.CLIAbort)
109+
self.assertEqual(result.exception.message, "A isn't a valid record type or zone is missing")
80110

81111
@mock.patch('SoftLayer.CLI.formatting.no_going_back')
82112
def test_delete_record(self, no_going_back_mock):

tests/managers/dns_tests.py

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,73 @@ def test_create_record(self):
9191
},))
9292
self.assertEqual(res, {'name': 'example.com'})
9393

94+
def test_create_record_mx(self):
95+
res = self.dns_client.create_record_mx(1, 'test', 'testing', ttl=1200, priority=21)
96+
97+
self.assert_called_with('SoftLayer_Dns_Domain_ResourceRecord',
98+
'createObject',
99+
args=({
100+
'domainId': 1,
101+
'ttl': 1200,
102+
'host': 'test',
103+
'type': 'MX',
104+
'data': 'testing',
105+
'mxPriority': 21
106+
},))
107+
self.assertEqual(res, {'name': 'example.com'})
108+
109+
def test_create_record_srv(self):
110+
res = self.dns_client.create_record_srv(1, 'record', 'test_data', 'SLS', 8080, 'foobar',
111+
ttl=1200, priority=21, weight=15)
112+
113+
self.assert_called_with('SoftLayer_Dns_Domain_ResourceRecord',
114+
'createObject',
115+
args=({
116+
'complexType': 'SoftLayer_Dns_Domain_ResourceRecord_SrvType',
117+
'domainId': 1,
118+
'ttl': 1200,
119+
'host': 'record',
120+
'type': 'SRV',
121+
'data': 'test_data',
122+
'priority': 21,
123+
'weight': 15,
124+
'service': 'foobar',
125+
'port': 8080,
126+
'protocol': 'SLS'
127+
},))
128+
self.assertEqual(res, {'name': 'example.com'})
129+
130+
def test_create_record_ptr(self):
131+
res = self.dns_client.create_record_ptr('test', 'testing', ttl=1200)
132+
133+
self.assert_called_with('SoftLayer_Dns_Domain_ResourceRecord',
134+
'createObject',
135+
args=({
136+
'ttl': 1200,
137+
'host': 'test',
138+
'type': 'PTR',
139+
'data': 'testing'
140+
},))
141+
self.assertEqual(res, {'name': 'example.com'})
142+
143+
def test_generate_create_dict(self):
144+
data = self.dns_client._generate_create_dict('foo', 'pmx', 'bar', 60, domainId=1234,
145+
mxPriority=18, port=80, protocol='TCP', weight=25)
146+
147+
assert_data = {
148+
'host': 'foo',
149+
'data': 'bar',
150+
'ttl': 60,
151+
'type': 'pmx',
152+
'domainId': 1234,
153+
'mxPriority': 18,
154+
'port': 80,
155+
'protocol': 'TCP',
156+
'weight': 25
157+
}
158+
159+
self.assertEqual(data, assert_data)
160+
94161
def test_delete_record(self):
95162
self.dns_client.delete_record(1)
96163

0 commit comments

Comments
 (0)