Skip to content

Commit

Permalink
Merge pull request quattor#87 in AQUILON_AQD/aqd from ~AQBLD/aqd:for_…
Browse files Browse the repository at this point in the history
…merge/master/by_topic/serviceaddr_to_sn to master

* commit '2431e18e14bcffb83e2ff380f320a653e251aef8':
  Allow service address PTR to point at shared name
  Refactor add_address_alias into dbwrappers/dns.py
  Fix minor formatting bug in notification error
  • Loading branch information
Tomasz Kotarba authored and Tomasz Kotarba committed Nov 21, 2019
2 parents 07b8cd8 + 2431e18 commit 6562ea0
Show file tree
Hide file tree
Showing 10 changed files with 414 additions and 77 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
58 changes: 11 additions & 47 deletions lib/aquilon/worker/commands/add_address_alias.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# -*- cpy-indent-level: 4; indent-tabs-mode: nil -*-
# ex: set expandtab softtabstop=4 shiftwidth=4:
#
# Copyright (C) 2008,2009,2010,2011,2012,2013,2014,2015,2016,2017,2018 Contributor
# Copyright (C) 2008-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 @@ -16,11 +16,12 @@
# limitations under the License.
"""Contains the logic for `aq add address alias`."""

from aquilon.exceptions_ import ArgumentError
from aquilon.aqdb.model import AddressAlias, Fqdn, ReservedName
from aquilon.aqdb.model import (
Fqdn,
)
from aquilon.worker.broker import BrokerCommand
from aquilon.worker.dbwrappers.grn import lookup_grn
from aquilon.worker.dbwrappers.change_management import ChangeManagement
from aquilon.worker.dbwrappers.dns import add_address_alias


class CommandAddAddressAlias(BrokerCommand):
Expand All @@ -37,17 +38,6 @@ def render(self, session, logger, fqdn, dns_environment, target,
dbfqdn = Fqdn.get_or_create(session, dns_environment=dns_environment,
fqdn=fqdn)

if dbfqdn.dns_environment.is_default and \
dbfqdn.dns_domain.name == "ms.com":
raise ArgumentError("%s record in DNS domain ms.com, DNS "
"environment %s is not allowed." %
(AddressAlias._get_class_label(),
dbfqdn.dns_environment.name))

if dbfqdn.dns_domain.restricted:
raise ArgumentError("{0} is restricted, aliases are not allowed."
.format(dbfqdn.dns_domain))

dbtarget = Fqdn.get_unique(session, fqdn=target,
dns_environment=target_environment,
compel=True)
Expand All @@ -57,37 +47,11 @@ def render(self, session, logger, fqdn, dns_environment, target,
cm.consider(dbtarget)
cm.validate()

dbgrn = None
if grn or eon_id:
dbgrn = lookup_grn(session, grn, eon_id, logger=logger,
config=self.config)

# Make sure all AddressAlias records under the same Fqdn has
# the same GRN.
for rec in dbfqdn.dns_records:
if isinstance(rec, AddressAlias):
if not dbgrn:
dbgrn = rec.owner_grn
elif dbgrn != rec.owner_grn:
raise ArgumentError("{0} with target {1} is set to a "
"different GRN."
.format(dbfqdn, rec.target.fqdn))
break

db_record = AddressAlias(fqdn=dbfqdn, target=dbtarget, ttl=ttl,
owner_grn=dbgrn, comments=comments,
require_grn=False)
session.add(db_record)

if exporter:
if any(
dr != db_record and
not isinstance(dr, ReservedName)
for dr in dbfqdn.dns_records):
exporter.update(dbfqdn)
else:
exporter.create(dbfqdn)

session.flush()
add_address_alias(session, logger, config=self.config,
dbsrcfqdn=dbfqdn,
dbtargetfqdn=dbtarget,
ttl=ttl, grn=grn, eon_id=eon_id,
comments=comments, exporter=exporter,
flush_session=True)

return
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
Loading

0 comments on commit 6562ea0

Please sign in to comment.