Skip to content

Commit 5b9f7de

Browse files
committed
Merge pull request softlayer#661 from allmightyspiff/issues192
issues192 added ability to sync aaaa records
2 parents 50c60bd + 0e2fd58 commit 5b9f7de

2 files changed

Lines changed: 232 additions & 10 deletions

File tree

SoftLayer/CLI/virt/dns.py

Lines changed: 57 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,29 +18,45 @@
1818
@click.option('--a-record', '-a',
1919
is_flag=True,
2020
help="Sync the A record for the host")
21+
@click.option('--aaaa-record',
22+
is_flag=True,
23+
help="Sync the AAAA record for the host")
2124
@click.option('--ptr', is_flag=True, help="Sync the PTR record for the host")
2225
@click.option('--ttl',
2326
default=7200,
2427
show_default=True,
2528
type=click.INT,
2629
help="Sets the TTL for the A and/or PTR records")
2730
@environment.pass_env
28-
def cli(env, identifier, a_record, ptr, ttl):
31+
def cli(env, identifier, a_record, aaaa_record, ptr, ttl):
2932
"""Sync DNS records."""
3033

34+
items = ['id',
35+
'globalIdentifier',
36+
'fullyQualifiedDomainName',
37+
'hostname',
38+
'domain',
39+
'primaryBackendIpAddress',
40+
'primaryIpAddress',
41+
'''primaryNetworkComponent[
42+
id, primaryIpAddress,
43+
primaryVersion6IpAddressRecord[ipAddress]
44+
]''']
45+
mask = "mask[%s]" % ','.join(items)
3146
dns = SoftLayer.DNSManager(env.client)
3247
vsi = SoftLayer.VSManager(env.client)
3348

3449
vs_id = helpers.resolve_id(vsi.resolve_ids, identifier, 'VS')
35-
instance = vsi.get_instance(vs_id)
50+
instance = vsi.get_instance(vs_id, mask=mask)
3651
zone_id = helpers.resolve_id(dns.resolve_ids,
3752
instance['domain'],
3853
name='zone')
3954

4055
def sync_a_record():
4156
"""Sync A record."""
42-
records = dns.get_records(zone_id, host=instance['hostname'])
43-
57+
records = dns.get_records(zone_id,
58+
host=instance['hostname'],
59+
record_type='a')
4460
if not records:
4561
# don't have a record, lets add one to the base zone
4662
dns.create_record(zone['id'],
@@ -49,15 +65,44 @@ def sync_a_record():
4965
instance['primaryIpAddress'],
5066
ttl=ttl)
5167
else:
52-
recs = [x for x in records if x['type'].lower() == 'a']
53-
if len(recs) != 1:
68+
if len(records) != 1:
5469
raise exceptions.CLIAbort("Aborting A record sync, found "
55-
"%d A record exists!" % len(recs))
56-
rec = recs[0]
70+
"%d A record exists!" % len(records))
71+
rec = records[0]
5772
rec['data'] = instance['primaryIpAddress']
5873
rec['ttl'] = ttl
5974
dns.edit_record(rec)
6075

76+
def sync_aaaa_record():
77+
"""Sync AAAA record."""
78+
records = dns.get_records(zone_id,
79+
host=instance['hostname'],
80+
record_type='aaaa')
81+
try:
82+
# done this way to stay within 80 character lines
83+
component = instance['primaryNetworkComponent']
84+
record = component['primaryVersion6IpAddressRecord']
85+
ip_address = record['ipAddress']
86+
except KeyError:
87+
raise exceptions.CLIAbort("%s does not have an ipv6 address"
88+
% instance['fullyQualifiedDomainName'])
89+
90+
if not records:
91+
# don't have a record, lets add one to the base zone
92+
dns.create_record(zone['id'],
93+
instance['hostname'],
94+
'aaaa',
95+
ip_address,
96+
ttl=ttl)
97+
else:
98+
if len(records) != 1:
99+
raise exceptions.CLIAbort("Aborting A record sync, found "
100+
"%d A record exists!" % len(records))
101+
rec = records[0]
102+
rec['data'] = ip_address
103+
rec['ttl'] = ttl
104+
dns.edit_record(rec)
105+
61106
def sync_ptr_record():
62107
"""Sync PTR record."""
63108
host_rec = instance['primaryIpAddress'].split('.')[-1]
@@ -94,11 +139,14 @@ def sync_ptr_record():
94139
raise exceptions.CLIAbort("Aborting DNS sync")
95140

96141
both = False
97-
if not ptr and not a_record:
142+
if not ptr and not a_record and not aaaa_record:
98143
both = True
99144

100145
if both or a_record:
101146
sync_a_record()
102147

103148
if both or ptr:
104149
sync_ptr_record()
150+
151+
if aaaa_record:
152+
sync_aaaa_record()

tests/CLI/modules/vs_tests.py

Lines changed: 175 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
:license: MIT, see LICENSE for more details.
66
"""
77
import mock
8-
98
from SoftLayer import testing
9+
from SoftLayer.CLI import exceptions
1010

1111
import json
1212

@@ -132,3 +132,177 @@ def test_create(self, confirm_mock):
132132
'networkComponents': [{'maxSpeed': '100'}]},)
133133
self.assert_called_with('SoftLayer_Virtual_Guest', 'createObject',
134134
args=args)
135+
136+
@mock.patch('SoftLayer.CLI.formatting.confirm')
137+
def test_dns_sync_both(self, confirm_mock):
138+
confirm_mock.return_value = True
139+
getReverseDomainRecords = self.set_mock('SoftLayer_Virtual_Guest',
140+
'getReverseDomainRecords')
141+
getReverseDomainRecords.return_value = [{
142+
'networkAddress': '172.16.240.2',
143+
'name': '2.240.16.172.in-addr.arpa',
144+
'resourceRecords': [{'data': 'test.softlayer.com.',
145+
'id': 100,
146+
'host': '12'}],
147+
'updateDate': '2013-09-11T14:36:57-07:00',
148+
'serial': 1234665663,
149+
'id': 123456,
150+
}]
151+
getResourceRecords = self.set_mock('SoftLayer_Dns_Domain',
152+
'getResourceRecords')
153+
getResourceRecords.return_value = []
154+
createAargs = ({
155+
'type': 'a',
156+
'host': 'vs-test1',
157+
'domainId': 98765,
158+
'data': '172.16.240.2',
159+
'ttl': 7200
160+
},)
161+
createPTRargs = ({
162+
'type': 'ptr',
163+
'host': '2',
164+
'domainId': 123456,
165+
'data': 'vs-test1.test.sftlyr.ws',
166+
'ttl': 7200
167+
},)
168+
result = self.run_command(['vs', 'dns-sync', '100'])
169+
self.assertEqual(result.exit_code, 0)
170+
self.assert_called_with('SoftLayer_Dns_Domain','getResourceRecords')
171+
self.assert_called_with('SoftLayer_Virtual_Guest','getReverseDomainRecords')
172+
self.assert_called_with('SoftLayer_Dns_Domain_ResourceRecord','createObject',args=createAargs)
173+
self.assert_called_with('SoftLayer_Dns_Domain_ResourceRecord','createObject',args=createPTRargs)
174+
175+
@mock.patch('SoftLayer.CLI.formatting.confirm')
176+
def test_dns_sync_v6(self, confirm_mock):
177+
confirm_mock.return_value = True
178+
getResourceRecords = self.set_mock('SoftLayer_Dns_Domain',
179+
'getResourceRecords')
180+
getResourceRecords.return_value = []
181+
createAargs = ({
182+
'type': 'a',
183+
'host': 'vs-test1',
184+
'domainId': 98765,
185+
'data': '172.16.240.2',
186+
'ttl': 7200
187+
},)
188+
guest = self.set_mock('SoftLayer_Virtual_Guest', 'getObject')
189+
test_guest = {
190+
'id' : 100,
191+
'hostname': 'vs-test1',
192+
'domain': 'sftlyr.ws',
193+
'primaryIpAddress': '172.16.240.2',
194+
'fullyQualifiedDomainName' : 'vs-test1.sftlyr.ws',
195+
"primaryNetworkComponent": {}
196+
}
197+
guest.return_value = test_guest
198+
result = self.run_command(['vs', 'dns-sync', '--aaaa-record', '100'])
199+
self.assertEqual(result.exit_code, 2)
200+
self.assertIsInstance(result.exception, exceptions.CLIAbort)
201+
202+
test_guest['primaryNetworkComponent'] = {
203+
'primaryVersion6IpAddressRecord' : {
204+
'ipAddress' : '2607:f0d0:1b01:0023:0000:0000:0000:0004'
205+
}
206+
}
207+
createV6args = ({
208+
'type': 'aaaa',
209+
'host': 'vs-test1',
210+
'domainId': 98765,
211+
'data': '2607:f0d0:1b01:0023:0000:0000:0000:0004',
212+
'ttl': 7200
213+
},)
214+
guest.return_value = test_guest
215+
result = self.run_command(['vs', 'dns-sync', '--aaaa-record', '100'])
216+
self.assertEqual(result.exit_code, 0)
217+
self.assert_called_with('SoftLayer_Dns_Domain_ResourceRecord','createObject',args=createV6args)
218+
219+
v6Record = {
220+
'id': 1,
221+
'ttl': 7200,
222+
'data': '2607:f0d0:1b01:0023:0000:0000:0000:0004',
223+
'host': 'vs-test1',
224+
'type': 'aaaa'
225+
}
226+
227+
getResourceRecords = self.set_mock('SoftLayer_Dns_Domain', 'getResourceRecords')
228+
getResourceRecords.return_value =[v6Record]
229+
editArgs = (v6Record,)
230+
result = self.run_command(['vs', 'dns-sync', '--aaaa-record', '100'])
231+
self.assertEqual(result.exit_code, 0)
232+
self.assert_called_with('SoftLayer_Dns_Domain_ResourceRecord','editObject',args=editArgs)
233+
234+
getResourceRecords = self.set_mock('SoftLayer_Dns_Domain', 'getResourceRecords')
235+
getResourceRecords.return_value =[v6Record, v6Record]
236+
result = self.run_command(['vs', 'dns-sync', '--aaaa-record', '100'])
237+
self.assertEqual(result.exit_code, 2)
238+
self.assertIsInstance(result.exception, exceptions.CLIAbort)
239+
240+
241+
@mock.patch('SoftLayer.CLI.formatting.confirm')
242+
def test_dns_sync_edit_a(self, confirm_mock):
243+
confirm_mock.return_value = True
244+
getResourceRecords = self.set_mock('SoftLayer_Dns_Domain',
245+
'getResourceRecords')
246+
getResourceRecords.return_value = [
247+
{'id': 1, 'ttl': 7200, 'data': '1.1.1.1', 'host': 'vs-test1', 'type': 'a'}
248+
]
249+
editArgs = (
250+
{'type': 'a', 'host': 'vs-test1','data': '172.16.240.2', 'id': 1, 'ttl': 7200},
251+
)
252+
result = self.run_command(['vs', 'dns-sync', '-a', '100'])
253+
self.assertEqual(result.exit_code, 0)
254+
self.assert_called_with('SoftLayer_Dns_Domain_ResourceRecord',
255+
'editObject',args=editArgs)
256+
257+
getResourceRecords = self.set_mock('SoftLayer_Dns_Domain','getResourceRecords')
258+
getResourceRecords.return_value = [
259+
{'id': 1, 'ttl': 7200, 'data': '1.1.1.1', 'host': 'vs-test1', 'type': 'a'},
260+
{'id': 2, 'ttl': 7200, 'data': '1.1.1.1', 'host': 'vs-test1', 'type': 'a'}
261+
]
262+
result = self.run_command(['vs', 'dns-sync', '-a', '100'])
263+
self.assertEqual(result.exit_code, 2)
264+
self.assertIsInstance(result.exception, exceptions.CLIAbort)
265+
266+
267+
@mock.patch('SoftLayer.CLI.formatting.confirm')
268+
def test_dns_sync_edit_ptr(self, confirm_mock):
269+
confirm_mock.return_value = True
270+
getReverseDomainRecords = self.set_mock('SoftLayer_Virtual_Guest',
271+
'getReverseDomainRecords')
272+
getReverseDomainRecords.return_value = [{
273+
'networkAddress': '172.16.240.2',
274+
'name': '2.240.16.172.in-addr.arpa',
275+
'resourceRecords': [{'data': 'test.softlayer.com.',
276+
'id': 100,
277+
'host': '2'}],
278+
'updateDate': '2013-09-11T14:36:57-07:00',
279+
'serial': 1234665663,
280+
'id': 123456,
281+
}]
282+
editArgs = ({'host': '2', 'data': 'vs-test1.test.sftlyr.ws', 'id': 100, 'ttl': 7200},)
283+
result = self.run_command(['vs', 'dns-sync', '--ptr', '100'])
284+
self.assertEqual(result.exit_code, 0)
285+
self.assert_called_with('SoftLayer_Dns_Domain_ResourceRecord','editObject',args=editArgs)
286+
287+
288+
@mock.patch('SoftLayer.CLI.formatting.confirm')
289+
def test_dns_sync_misc_exception(self, confirm_mock):
290+
confirm_mock.return_value = False
291+
result = self.run_command(['vs', 'dns-sync', '-a', '100'])
292+
self.assertEqual(result.exit_code, 2)
293+
self.assertIsInstance(result.exception, exceptions.CLIAbort)
294+
295+
guest = self.set_mock('SoftLayer_Virtual_Guest', 'getObject')
296+
test_guest = {
297+
'id' : 100,
298+
'primaryIpAddress' :'',
299+
'hostname': 'vs-test1',
300+
'domain': 'sftlyr.ws',
301+
'fullyQualifiedDomainName' : 'vs-test1.sftlyr.ws',
302+
"primaryNetworkComponent": {}
303+
}
304+
guest.return_value = test_guest
305+
result = self.run_command(['vs', 'dns-sync', '-a', '100'])
306+
self.assertEqual(result.exit_code, 2)
307+
self.assertIsInstance(result.exception, exceptions.CLIAbort)
308+

0 commit comments

Comments
 (0)