Skip to content

Commit

Permalink
Allow service address PTR to point at shared name
Browse files Browse the repository at this point in the history
This adds an option --map_to_shared_name option for service addition and
update that causes the reverse PTR DNS record to point back to the FQDN
of the shared-name (within a resource-group).  If the 'sa_aliases' flag
is set for the shared-name, an address alias from the shared-name to the
service address is created (and removed on service address deletion).

Change-Id: Icdf078985321a5d035f3d5ac41a45b3f1e4d2393
Addresses-Issue: Jira/AQUILON-6374
Tested-by: Aquilon Template Build testing and verification
Reviewed-by: Tomasz Kotarba <Tomasz.Kotarba@morganstanley.com>
  • Loading branch information
Fred Barnes authored and Fred Barnes committed Nov 20, 2019
1 parent 8def9a5 commit 2431e18
Show file tree
Hide file tree
Showing 7 changed files with 324 additions and 23 deletions.
2 changes: 2 additions & 0 deletions etc/input.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6425,6 +6425,7 @@
</option>
<option name="comments" type="string">Comments</option>
<option name="map_to_primary" type="boolean">Should the reverse PTR point to the primary name?</option>
<option name="map_to_shared_name" type="boolean">Should the reverse PTR point to a shared name? (within a resource-group only)</option>
<option name="shared" type="flag">Allow the address to be used multiple times</option>
<option name="justification" type="string">Authorization tokens (e.g. TCM number or "emergency") to validate the request</option>
<option name="reason" type="string">Human readable description of why the operation was performed</option>
Expand Down Expand Up @@ -6507,6 +6508,7 @@
<option name="network_environment" type="string" requires="ip">Network environment (default: internal)</option>
<option name="comments" type="string">Comments</option>
<option name="map_to_primary" type="boolean">Should the reverse PTR point to the primary name?</option>
<option name="map_to_shared_name" type="boolean">Should the reverse PTR point to a shared name? (within a resource-group only)</option>
<option name="justification" type="string">Authorization tokens (e.g. TCM number or "emergency") to validate the request</option>
<option name="reason" type="string">Human readable description of why the operation was performed</option>
<option name="cm_check" type="flag">Do a dry-run, and report the objects in-scope for change-management.</option>
Expand Down
80 changes: 67 additions & 13 deletions lib/aquilon/worker/commands/add_service_address.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,29 +17,45 @@
# limitations under the License.
"""Contains the logic for `aq add service address`."""

from aquilon.exceptions_ import ArgumentError
from aquilon.aqdb.column_types import AqStr
from aquilon.aqdb.model import ServiceAddress, Host, Fqdn, DnsDomain, Bunker
from aquilon.aqdb.model import (
BundleResource,
Bunker,
DnsDomain,
Fqdn,
Host,
ResourceGroup,
ServiceAddress,
SharedServiceName,
)
from aquilon.exceptions_ import ArgumentError
from aquilon.utils import validate_nlist_key
from aquilon.worker.broker import BrokerCommand
from aquilon.worker.dbwrappers.dns import grab_address
from aquilon.worker.dbwrappers.interface import get_interfaces, generate_ip
from aquilon.worker.dbwrappers.change_management import ChangeManagement
from aquilon.worker.dbwrappers.dns import (
add_address_alias,
grab_address,
)
from aquilon.worker.dbwrappers.interface import (
generate_ip,
get_interfaces,
)
from aquilon.worker.dbwrappers.location import get_default_dns_domain
from aquilon.worker.dbwrappers.resources import get_resource_holder
from aquilon.worker.dbwrappers.search import search_next
from aquilon.worker.processes import DSDBRunner
from aquilon.worker.dbwrappers.change_management import ChangeManagement


class CommandAddServiceAddress(BrokerCommand):
requires_plenaries = True

required_parameters = ["name"]

def render(self, session, logger, plenaries, service_address, shortname, prefix,
dns_domain, ip, ipfromtype, name, interfaces, hostname, cluster, metacluster,
resourcegroup, network_environment, map_to_primary, shared,
comments, user, justification, reason, exporter,
def render(self, session, logger, plenaries, service_address, shortname,
prefix, dns_domain, ip, ipfromtype, name, interfaces,
hostname, cluster, metacluster, resourcegroup,
network_environment, map_to_primary, map_to_shared_name,
shared, comments, user, justification, reason, exporter,
default_dns_domain_from, **kwargs):
"""Extend the superclass method to render this command.
Expand All @@ -64,6 +80,8 @@ def render(self, session, logger, plenaries, service_address, shortname, prefix,
:param network_environment: a network environment (default: internal)
:param map_to_primary: True if the reverse PTR should point to the
primary name
:param map_to_shared_name: True if the reverse PTR should point to
a shared-name within the same resourcegroup
:param shared: allow the address to be used multiple times
:param comments: a string with comments
:param user: a string with the principal / user who invoked the command
Expand Down Expand Up @@ -159,6 +177,16 @@ def render(self, session, logger, plenaries, service_address, shortname, prefix,

ip = generate_ip(session, logger, None, net_location_set, ip=ip, ipfromtype=ipfromtype)

# if in a resource-group, look for a sibling SharedServiceName resource
sibling_ssn = None
if (isinstance(holder, BundleResource) and
isinstance(holder.resourcegroup, ResourceGroup)):
for res in holder.resources:
if isinstance(res, SharedServiceName):
# this one
sibling_ssn = res
break

# TODO: add allow_multi=True
dbdns_rec, newly_created = grab_address(session, service_address, ip,
network_environment,
Expand All @@ -167,11 +195,27 @@ def render(self, session, logger, plenaries, service_address, shortname, prefix,
require_grn=False)
ip = dbdns_rec.ip

if map_to_primary:
if not isinstance(toplevel_holder, Host):
if map_to_primary and map_to_shared_name:
raise ArgumentError("Cannot use --map_to_primary and "
"--map_to_shared_name together")
elif map_to_shared_name:
# if the holder is a resource-group that has a SharedServiceName
# resource, then set the PTR record as the SharedServiceName's FQDN
if sibling_ssn:
dbdns_rec.reverse_ptr = sibling_ssn.fqdn
else:
raise ArgumentError("--map_to_shared_name specified, but no "
"shared service name in {0:l}".
format(holder))
elif map_to_primary:
if isinstance(toplevel_holder, Host):
dbdns_rec.reverse_ptr = \
toplevel_holder.hardware_entity.primary_name.fqdn
else:
raise ArgumentError("The --map_to_primary option works only "
"for host-based service addresses.")
dbdns_rec.reverse_ptr = toplevel_holder.hardware_entity.primary_name.fqdn
"for host-based service addresses or "
"within a resource-group where a "
"SharedServiceName resource exists.")

dbifaces = []
if interfaces:
Expand All @@ -193,6 +237,16 @@ def render(self, session, logger, plenaries, service_address, shortname, prefix,

session.flush()

# if we have a sibling SharedServiceName where service-address
# aliases is set, add a new address-alias pointing at the IP
if sibling_ssn and sibling_ssn.sa_aliases:
add_address_alias(session, logger, config=self.config,
dbsrcfqdn=sibling_ssn.fqdn,
dbtargetfqdn=dbdns_rec.fqdn,
ttl=None, grn=None, eon_id=None,
comments=None, exporter=exporter,
flush_session=True)

plenaries.add(holder.holder_object)
plenaries.add(dbsrv)

Expand Down
37 changes: 34 additions & 3 deletions lib/aquilon/worker/commands/del_service_address.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
#!/usr/bin/env python
# -*- cpy-indent-level: 4; indent-tabs-mode: nil -*-
# ex: set expandtab softtabstop=4 shiftwidth=4:
#
# Copyright (C) 2012,2013,2014,2015,2016,2017,2018 Contributor
# Copyright (C) 2012-2019 Contributor
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
Expand All @@ -15,14 +16,20 @@
# See the License for the specific language governing permissions and
# limitations under the License.

from aquilon.aqdb.model import (
AddressAlias,
BundleResource,
ResourceGroup,
ServiceAddress,
SharedServiceName,
)
from aquilon.exceptions_ import ArgumentError
from aquilon.aqdb.model import ServiceAddress
from aquilon.worker.broker import BrokerCommand
from aquilon.worker.dbwrappers.change_management import ChangeManagement
from aquilon.worker.dbwrappers.dns import delete_dns_record
from aquilon.worker.dbwrappers.resources import get_resource_holder
from aquilon.worker.dbwrappers.service_instance import check_no_provided_service
from aquilon.worker.processes import DSDBRunner
from aquilon.worker.dbwrappers.change_management import ChangeManagement


class CommandDelServiceAddress(BrokerCommand):
Expand Down Expand Up @@ -59,6 +66,30 @@ def render(self, session, logger, plenaries, name, hostname, cluster, metacluste

holder.resources.remove(dbsrv)
if not dbdns_rec.service_addresses:
# if we're in a resource-group and a shared-service-name exists
# that has sa_aliases set, and there'a an alias pointing at
# ourselves, remove it.

sibling_ssn = None
if (isinstance(holder, BundleResource) and
isinstance(holder.resourcegroup, ResourceGroup)):
for res in holder.resources:
if isinstance(res, SharedServiceName):
# this one
sibling_ssn = res
break

if sibling_ssn and sibling_ssn.sa_aliases:
# look for one match against this target only
for rr in sibling_ssn.fqdn.dns_records:
if not isinstance(rr, AddressAlias):
continue
if rr.target != dbdns_rec.fqdn:
continue

delete_dns_record(rr, exporter=exporter)
break

delete_dns_record(dbdns_rec, exporter=exporter)

session.flush()
Expand Down
42 changes: 37 additions & 5 deletions lib/aquilon/worker/commands/update_service_address.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
#!/usr/bin/env python
# -*- cpy-indent-level: 4; indent-tabs-mode: nil -*-
# ex: set expandtab softtabstop=4 shiftwidth=4:
#
# Copyright (C) 2015,2016,2017 Contributor
# Copyright (C) 2015-2017,2019 Contributor
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
Expand All @@ -15,9 +16,16 @@
# See the License for the specific language governing permissions and
# limitations under the License.

from aquilon.exceptions_ import ArgumentError
from aquilon.aqdb.model import ServiceAddress, Host, NetworkEnvironment
from aquilon.aqdb.model import (
BundleResource,
Host,
NetworkEnvironment,
ResourceGroup,
ServiceAddress,
SharedServiceName,
)
from aquilon.aqdb.model.network import get_net_id_from_ip
from aquilon.exceptions_ import ArgumentError
from aquilon.worker.broker import BrokerCommand
from aquilon.worker.dbwrappers.dns import update_address
from aquilon.worker.dbwrappers.interface import get_interfaces
Expand All @@ -31,8 +39,9 @@ class CommandUpdateServiceAddress(BrokerCommand):

required_parameters = ["name"]

def render(self, session, logger, plenaries, ip, name, interfaces, hostname, cluster,
metacluster, resourcegroup, network_environment, map_to_primary,
def render(self, session, logger, plenaries, ip, name, interfaces,
hostname, cluster, metacluster, resourcegroup,
network_environment, map_to_primary, map_to_shared_name,
comments, user, justification, reason, **arguments):
holder = get_resource_holder(session, logger, hostname, cluster,
metacluster, resourcegroup, compel=True)
Expand Down Expand Up @@ -73,6 +82,10 @@ def render(self, session, logger, plenaries, ip, name, interfaces, hostname, clu
if comments is not None:
dbsrv.comments = comments

if map_to_primary and map_to_shared_name:
raise ArgumentError("Cannot use --map_to_primary and "
"--map_to_shared_name together")

if map_to_primary is not None:
if not isinstance(toplevel_holder, Host):
raise ArgumentError("The --map_to_primary option works only "
Expand All @@ -82,6 +95,25 @@ def render(self, session, logger, plenaries, ip, name, interfaces, hostname, clu
else:
dbsrv.dns_record.reverse_ptr = None

if map_to_shared_name:
# if the holder is a resource-group that has a SharedServiceName
# resource, then set the PTR record as the SharedServiceName's FQDN

sibling_ssn = None
if (isinstance(holder, BundleResource) and
isinstance(holder.resourcegroup, ResourceGroup)):
for res in holder.resources:
if isinstance(res, SharedServiceName):
# this one
sibling_ssn = res
break

if sibling_ssn:
dbsrv.dns_record.reverse_ptr = sibling_ssn.fqdn
else:
raise ArgumentError("--map_to_shared_name specified, but no "
"shared service name")

session.flush()

with plenaries.get_key():
Expand Down
6 changes: 4 additions & 2 deletions tests/broker/orderedsuite.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@
from .test_add_sandbox import TestAddSandbox
from .test_add_service import TestAddService
from .test_add_service_address import TestAddServiceAddress
from .test_add_service_address_sn_aliases import TestAddServiceAddressSNAliases
from .test_add_share import TestAddShare
from .test_add_shared_service_name import TestAddSharedServiceName
from .test_add_srv_record import TestAddSrvRecord
Expand Down Expand Up @@ -204,6 +205,7 @@
from .test_del_sandbox import TestDelSandbox
from .test_del_service import TestDelService
from .test_del_service_address import TestDelServiceAddress
from .test_del_service_address_sn_aliases import TestDelServiceAddressSNAliases
from .test_del_share import TestDelShare
from .test_del_shared_service_name import TestDelSharedServiceName
from .test_del_srv_record import TestDelSrvRecord
Expand Down Expand Up @@ -406,7 +408,7 @@ class BrokerTestSuite(unittest.TestSuite):
TestAddResourceGroup, TestAddShare, TestAddFilesystem,
TestAddApplication, TestAddIntervention,
TestAddHostlink, TestAddRebootSchedule, TestAddRebootIntervention,
TestAddSharedServiceName,
TestAddSharedServiceName, TestAddServiceAddressSNAliases,
TestFlush,
TestMakeAquilon, TestMakeCluster, TestCluster,
TestAddAllowedPersonality,
Expand Down Expand Up @@ -504,7 +506,7 @@ class BrokerTestSuite(unittest.TestSuite):
TestUnbindFeature,
TestDel10GigHardware, TestDelVirtualHardware,
TestUnbindCluster, TestUncluster,
TestDelSharedServiceName,
TestDelServiceAddressSNAliases, TestDelSharedServiceName,
TestDelShare, TestDelFilesystem,
TestDelHostlink, TestDelRebootIntervention, TestDelRebootSchedule,
TestDelIntervention, TestDelApplication,
Expand Down
Loading

0 comments on commit 2431e18

Please sign in to comment.