diff --git a/drydock_provisioner/drivers/node/maasdriver/actions/node.py b/drydock_provisioner/drivers/node/maasdriver/actions/node.py index 20f28622..2535af01 100644 --- a/drydock_provisioner/drivers/node/maasdriver/actions/node.py +++ b/drydock_provisioner/drivers/node/maasdriver/actions/node.py @@ -1042,7 +1042,8 @@ class IdentifyNode(BaseMaasAction): for n in nodes: try: - machine = machine_list.identify_baremetal_node(n) + machine = machine_list.identify_baremetal_node(n, + domain=n.get_domain(site_design)) if machine is not None: self.task.success(focus=n.get_id()) self.task.add_status_msg( diff --git a/drydock_provisioner/drivers/node/maasdriver/models/machine.py b/drydock_provisioner/drivers/node/maasdriver/models/machine.py index 93419460..1d8249ce 100644 --- a/drydock_provisioner/drivers/node/maasdriver/models/machine.py +++ b/drydock_provisioner/drivers/node/maasdriver/models/machine.py @@ -47,8 +47,9 @@ class Machine(model_base.ResourceBase): 'owner_data', 'block_devices', 'volume_groups', + 'domain' ] - json_fields = ['hostname', 'power_type'] + json_fields = ['hostname', 'power_type', 'domain'] def __init__(self, api_client, **kwargs): super(Machine, self).__init__(api_client, **kwargs) @@ -533,7 +534,7 @@ class Machines(model_base.ResourceCollectionBase): return node - def identify_baremetal_node(self, node_model, update_name=True): + def identify_baremetal_node(self, node_model, update_name=True, domain="local"): """Find MaaS node resource matching Drydock BaremetalNode. Search all the defined MaaS Machines and attempt to match @@ -583,6 +584,7 @@ class Machines(model_base.ResourceCollectionBase): if maas_node.hostname != node_model.name and update_name: try: maas_node.hostname = node_model.name + maas_node.domain = domain maas_node.update() if node_model.oob_type == 'libvirt': self.logger.debug( diff --git a/drydock_provisioner/objects/node.py b/drydock_provisioner/objects/node.py index 481765b2..2fb3df5d 100644 --- a/drydock_provisioner/objects/node.py +++ b/drydock_provisioner/objects/node.py @@ -94,6 +94,41 @@ class BaremetalNode(drydock_provisioner.objects.hostprofile.HostProfile): return + def get_domain(self, site_design): + """Return the domain for this node. + + The domain for this is the DNS domain of the primary network or local. + + :param SiteDesign site_design: A instance containing definitions for the networks + this node is attached to. + """ + try: + pn = site_design.get_network(self.primary_network) + domain = pn.dns_domain or "local" + except errors.DesignError as dex: + self.logger.debug("Primary network not found, use domain 'local'.") + domain = "local" + except AttributeError as aex: + self.logger.debug("Primary network does not define a domain, use domain 'local'.") + domain = "local" + + return domain + + def get_fqdn(self, site_design): + """Returns the FQDN for this node. + + The FQDN for this node is composed of the node hostname ``self.name`` appended + with the domain name of the primary network if defined. If the primary network + does not define a domain name, the domain is ``local``. + + :param site_design: A SiteDesign instance containing definitions for the networks + the node is attached to. + """ + hostname = self.name + domain = self.get_domain(site_design) + + return "{}.{}".format(hostname, domain) + def resolve_kernel_params(self, site_design): """Check if any kernel parameter values are supported references.""" if not self.hardware_profile: diff --git a/tests/unit/test_node_naming.py b/tests/unit/test_node_naming.py new file mode 100644 index 00000000..8f0a0a35 --- /dev/null +++ b/tests/unit/test_node_naming.py @@ -0,0 +1,66 @@ +# Copyright 2018 AT&T Intellectual Property. All other rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Test node model hostname and FQDN.""" + +import drydock_provisioner.objects as objects + + +class TestNodeNaming(object): + def test_node_fqdn(self, deckhand_orchestrator, input_files, setup): + """Test fqdn rendering.""" + input_file = input_files.join("deckhand_fullsite.yaml") + + design_ref = "file://%s" % str(input_file) + + design_status, design_data = deckhand_orchestrator.get_effective_site( + design_ref) + + assert design_status.status == objects.fields.ValidationResult.Success + + # From the sample YAML, the domain assigned to the 'mgmt' network + # is 'mgmt.sitename.example.com'. This is the primary network for + # 'controller01' + + nodename = 'controller01' + expected_fqdn = '{}.{}'.format(nodename, 'mgmt.sitename.example.com') + + node_model = design_data.get_baremetal_node(nodename) + + assert node_model + + assert node_model.get_fqdn(design_data) == expected_fqdn + + def test_node_domain(self, deckhand_orchestrator, input_files, setup): + """Test domain rendering.""" + input_file = input_files.join("deckhand_fullsite.yaml") + + design_ref = "file://%s" % str(input_file) + + design_status, design_data = deckhand_orchestrator.get_effective_site( + design_ref) + + assert design_status.status == objects.fields.ValidationResult.Success + + # From the sample YAML, the domain assigned to the 'mgmt' network + # is 'mgmt.sitename.example.com'. This is the primary network for + # 'controller01' + + node_name = 'controller01' + expected_domain = 'mgmt.sitename.example.com' + + node_model = design_data.get_baremetal_node(node_name) + + assert node_model + + assert node_model.get_domain(design_data) == expected_domain