Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Fix #1195 slcli vs dns-sync --ptr fails #1207

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Jan 14, 2020
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
64 changes: 64 additions & 0 deletions SoftLayer/CLI/hardware/dns.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
"""Sync DNS records."""
# :license: MIT, see LICENSE for more details.
# pylint: disable=duplicate-code

import click

import SoftLayer
from SoftLayer.CLI import environment
from SoftLayer.CLI import exceptions
from SoftLayer.CLI import formatting
from SoftLayer.CLI import helpers


@click.command(epilog="""If you don't specify any
arguments, it will attempt to update both the A and PTR records. If you don't
want to update both records, you may use the -a or --ptr arguments to limit
the records updated.""")
@click.argument('identifier')
@click.option('--a-record', '-a', is_flag=True, help="Sync the A record for the host")
@click.option('--aaaa-record', is_flag=True, help="Sync the AAAA record for the host")
@click.option('--ptr', is_flag=True, help="Sync the PTR record for the host")
@click.option('--ttl', default=7200, show_default=True, type=click.INT,
help="Sets the TTL for the A and/or PTR records")
@environment.pass_env
def cli(env, identifier, a_record, aaaa_record, ptr, ttl):
"""Sync DNS records."""

mask = """mask[id, globalIdentifier, fullyQualifiedDomainName, hostname, domain,
primaryBackendIpAddress,primaryIpAddress,
primaryNetworkComponent[id,primaryIpAddress,primaryVersion6IpAddressRecord[ipAddress]]]"""
dns = SoftLayer.DNSManager(env.client)
server = SoftLayer.HardwareManager(env.client)

server_id = helpers.resolve_id(server.resolve_ids, identifier, 'VS')
instance = server.get_hardware(server_id, mask=mask)
zone_id = helpers.resolve_id(dns.resolve_ids, instance['domain'], name='zone')

if not instance['primaryIpAddress']:
raise exceptions.CLIAbort('No primary IP address associated with this hardware')

go_for_it = env.skip_confirmations or formatting.confirm(
"Attempt to update DNS records for %s" % instance['fullyQualifiedDomainName'])

if not go_for_it:
raise exceptions.CLIAbort("Aborting DNS sync")

# both will be true only if no options are passed in, basically.
both = (not ptr) and (not a_record) and (not aaaa_record)

if both or a_record:
dns.sync_host_record(zone_id, instance['hostname'], instance['primaryIpAddress'], 'a', ttl)

if both or ptr:
# getReverseDomainRecords returns a list of 1 element, so just get the top.
ptr_domains = env.client['Hardware_Server'].getReverseDomainRecords(id=instance['id']).pop()
dns.sync_ptr_record(ptr_domains, instance['primaryIpAddress'], instance['fullyQualifiedDomainName'], ttl)

if aaaa_record:
try:
# done this way to stay within 80 character lines
ipv6 = instance['primaryNetworkComponent']['primaryVersion6IpAddressRecord']['ipAddress']
dns.sync_host_record(zone_id, instance['hostname'], ipv6, 'aaaa', ttl)
except KeyError:
raise exceptions.CLIAbort("%s does not have an ipv6 address" % instance['fullyQualifiedDomainName'])
1 change: 1 addition & 0 deletions SoftLayer/CLI/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@
('hardware:rescue', 'SoftLayer.CLI.hardware.power:rescue'),
('hardware:ready', 'SoftLayer.CLI.hardware.ready:cli'),
('hardware:toggle-ipmi', 'SoftLayer.CLI.hardware.toggle_ipmi:cli'),
('hardware:dns-sync', 'SoftLayer.CLI.hardware.dns:cli'),

('securitygroup', 'SoftLayer.CLI.securitygroup'),
('securitygroup:list', 'SoftLayer.CLI.securitygroup.list:cli'),
Expand Down
138 changes: 25 additions & 113 deletions SoftLayer/CLI/virt/dns.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""Sync DNS records."""
# :license: MIT, see LICENSE for more details.
# pylint: disable=duplicate-code

import click

Expand All @@ -15,138 +16,49 @@
want to update both records, you may use the -a or --ptr arguments to limit
the records updated.""")
@click.argument('identifier')
@click.option('--a-record', '-a',
is_flag=True,
help="Sync the A record for the host")
@click.option('--aaaa-record',
is_flag=True,
help="Sync the AAAA record for the host")
@click.option('--a-record', '-a', is_flag=True, help="Sync the A record for the host")
@click.option('--aaaa-record', is_flag=True, help="Sync the AAAA record for the host")
@click.option('--ptr', is_flag=True, help="Sync the PTR record for the host")
@click.option('--ttl',
default=7200,
show_default=True,
type=click.INT,
@click.option('--ttl', default=7200, show_default=True, type=click.INT,
help="Sets the TTL for the A and/or PTR records")
@environment.pass_env
def cli(env, identifier, a_record, aaaa_record, ptr, ttl):
"""Sync DNS records."""

items = ['id',
'globalIdentifier',
'fullyQualifiedDomainName',
'hostname',
'domain',
'primaryBackendIpAddress',
'primaryIpAddress',
'''primaryNetworkComponent[
id, primaryIpAddress,
primaryVersion6IpAddressRecord[ipAddress]
]''']
mask = "mask[%s]" % ','.join(items)
mask = """mask[id, globalIdentifier, fullyQualifiedDomainName, hostname, domain,
primaryBackendIpAddress,primaryIpAddress,
primaryNetworkComponent[id,primaryIpAddress,primaryVersion6IpAddressRecord[ipAddress]]]"""
dns = SoftLayer.DNSManager(env.client)
vsi = SoftLayer.VSManager(env.client)
server = SoftLayer.VSManager(env.client)

vs_id = helpers.resolve_id(vsi.resolve_ids, identifier, 'VS')
instance = vsi.get_instance(vs_id, mask=mask)
zone_id = helpers.resolve_id(dns.resolve_ids,
instance['domain'],
name='zone')

def sync_a_record():
"""Sync A record."""
records = dns.get_records(zone_id,
host=instance['hostname'],
record_type='a')
if not records:
# don't have a record, lets add one to the base zone
dns.create_record(zone['id'],
instance['hostname'],
'a',
instance['primaryIpAddress'],
ttl=ttl)
else:
if len(records) != 1:
raise exceptions.CLIAbort("Aborting A record sync, found "
"%d A record exists!" % len(records))
rec = records[0]
rec['data'] = instance['primaryIpAddress']
rec['ttl'] = ttl
dns.edit_record(rec)

def sync_aaaa_record():
"""Sync AAAA record."""
records = dns.get_records(zone_id,
host=instance['hostname'],
record_type='aaaa')
try:
# done this way to stay within 80 character lines
component = instance['primaryNetworkComponent']
record = component['primaryVersion6IpAddressRecord']
ip_address = record['ipAddress']
except KeyError:
raise exceptions.CLIAbort("%s does not have an ipv6 address"
% instance['fullyQualifiedDomainName'])

if not records:
# don't have a record, lets add one to the base zone
dns.create_record(zone['id'],
instance['hostname'],
'aaaa',
ip_address,
ttl=ttl)
else:
if len(records) != 1:
raise exceptions.CLIAbort("Aborting A record sync, found "
"%d A record exists!" % len(records))
rec = records[0]
rec['data'] = ip_address
rec['ttl'] = ttl
dns.edit_record(rec)

def sync_ptr_record():
"""Sync PTR record."""
host_rec = instance['primaryIpAddress'].split('.')[-1]
ptr_domains = (env.client['Virtual_Guest']
.getReverseDomainRecords(id=instance['id'])[0])
edit_ptr = None
for ptr in ptr_domains['resourceRecords']:
if ptr['host'] == host_rec:
ptr['ttl'] = ttl
edit_ptr = ptr
break

if edit_ptr:
edit_ptr['data'] = instance['fullyQualifiedDomainName']
dns.edit_record(edit_ptr)
else:
dns.create_record(ptr_domains['id'],
host_rec,
'ptr',
instance['fullyQualifiedDomainName'],
ttl=ttl)
server_id = helpers.resolve_id(server.resolve_ids, identifier, 'VS')
instance = server.get_instance(server_id, mask=mask)
zone_id = helpers.resolve_id(dns.resolve_ids, instance['domain'], name='zone')

if not instance['primaryIpAddress']:
raise exceptions.CLIAbort('No primary IP address associated with '
'this VS')

zone = dns.get_zone(zone_id)
raise exceptions.CLIAbort('No primary IP address associated with this VS')

go_for_it = env.skip_confirmations or formatting.confirm(
"Attempt to update DNS records for %s"
% instance['fullyQualifiedDomainName'])
"Attempt to update DNS records for %s" % instance['fullyQualifiedDomainName'])

if not go_for_it:
raise exceptions.CLIAbort("Aborting DNS sync")

both = False
if not ptr and not a_record and not aaaa_record:
both = True
# both will be true only if no options are passed in, basically.
both = (not ptr) and (not a_record) and (not aaaa_record)

if both or a_record:
sync_a_record()
dns.sync_host_record(zone_id, instance['hostname'], instance['primaryIpAddress'], 'a', ttl)

if both or ptr:
sync_ptr_record()
# getReverseDomainRecords returns a list of 1 element, so just get the top.
ptr_domains = env.client['Virtual_Guest'].getReverseDomainRecords(id=instance['id']).pop()
dns.sync_ptr_record(ptr_domains, instance['primaryIpAddress'], instance['fullyQualifiedDomainName'], ttl)

if aaaa_record:
sync_aaaa_record()
try:
# done this way to stay within 80 character lines
ipv6 = instance['primaryNetworkComponent']['primaryVersion6IpAddressRecord']['ipAddress']
dns.sync_host_record(zone_id, instance['hostname'], ipv6, 'aaaa', ttl)
except KeyError:
raise exceptions.CLIAbort("%s does not have an ipv6 address" % instance['fullyQualifiedDomainName'])
52 changes: 48 additions & 4 deletions SoftLayer/managers/dns.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"""
import time

from SoftLayer import exceptions
from SoftLayer import utils


Expand Down Expand Up @@ -205,13 +206,11 @@ def get_records(self, zone_id, ttl=None, data=None, host=None,
_filter['resourceRecords']['data'] = utils.query_filter(data)

if record_type:
_filter['resourceRecords']['type'] = utils.query_filter(
record_type.lower())
_filter['resourceRecords']['type'] = utils.query_filter(record_type.lower())

results = self.service.getResourceRecords(
id=zone_id,
mask='id,expire,domainId,host,minimum,refresh,retry,'
'mxPriority,ttl,type,data,responsiblePerson',
mask='id,expire,domainId,host,minimum,refresh,retry,mxPriority,ttl,type,data,responsiblePerson',
filter=_filter.to_dict(),
)

Expand All @@ -226,6 +225,7 @@ def edit_record(self, record):
:param dict record: the record to update

"""
record.pop('isGatewayAddress', None)
self.record.editObject(record, id=record['id'])

def dump_zone(self, zone_id):
Expand All @@ -235,3 +235,47 @@ def dump_zone(self, zone_id):

"""
return self.service.getZoneFileContents(id=zone_id)

def sync_host_record(self, zone_id, hostname, ip_address, record_type='a', ttl=7200):
"""For a given zone_id, will set hostname's A record to ip_address

:param integer zone_id: The zone id for the domain
:param string hostname: host part of the record
:param string ip_address: data part of the record
:param integer ttl: TTL for the record
:param string record_type: 'a' or 'aaaa'
"""
records = self.get_records(zone_id, host=hostname, record_type=record_type)
if not records:
# don't have a record, lets add one to the base zone
self.create_record(zone_id, hostname, record_type, ip_address, ttl=ttl)
else:
if len(records) != 1:
raise exceptions.SoftLayerError("Aborting record sync, found %d records!" % len(records))
rec = records[0]
rec['data'] = ip_address
rec['ttl'] = ttl
self.edit_record(rec)

def sync_ptr_record(self, ptr_domains, ip_address, fqdn, ttl=7200):
"""Sync PTR record.

:param dict ptr_domains: result from SoftLayer_Virtual_Guest.getReverseDomainRecords or
SoftLayer_Hardware_Server.getReverseDomainRecords
:param string ip_address: ip address to sync with
:param string fqdn: Fully Qualified Domain Name
:param integer ttl: TTL for the record
"""
host_rec = ip_address.split('.')[-1]
edit_ptr = None
for ptr in ptr_domains['resourceRecords']:
if ptr.get('host', '') == host_rec:
ptr['ttl'] = ttl
edit_ptr = ptr
break

if edit_ptr:
edit_ptr['data'] = fqdn
self.edit_record(edit_ptr)
else:
self.create_record(ptr_domains['id'], host_rec, 'ptr', fqdn, ttl=ttl)
3 changes: 3 additions & 0 deletions docs/cli/hardware.rst
Original file line number Diff line number Diff line change
Expand Up @@ -94,3 +94,6 @@ This function updates the firmware of a server. If already at the latest version
:prog: hw ready
:show-nested:

.. click:: SoftLayer.CLI.hardware.dns-sync:cli
:prog: hw dns-sync
:show-nested:
Loading