diff --git a/drydock_provisioner/objects/site.py b/drydock_provisioner/objects/site.py index 4bab9c1b..e6bac696 100644 --- a/drydock_provisioner/objects/site.py +++ b/drydock_provisioner/objects/site.py @@ -203,9 +203,10 @@ class SiteDesign(base.DrydockPersistentObject, base.DrydockObject): self.networks.append(new_network) def get_network(self, network_key): - for n in self.networks: - if n.get_id() == network_key: - return n + if self.networks: + for n in self.networks: + if n.get_id() == network_key: + return n raise errors.DesignError( "Network %s not found in design state" % network_key) @@ -220,9 +221,10 @@ class SiteDesign(base.DrydockPersistentObject, base.DrydockObject): self.network_links.append(new_network_link) def get_network_link(self, link_key): - for l in self.network_links: - if l.get_id() == link_key: - return l + if self.network_links: + for l in self.network_links: + if l.get_id() == link_key: + return l raise errors.DesignError( "NetworkLink %s not found in design state" % link_key) @@ -237,9 +239,10 @@ class SiteDesign(base.DrydockPersistentObject, base.DrydockObject): self.racks.append(new_rack) def get_rack(self, rack_key): - for r in self.racks: - if r.get_id() == rack_key: - return r + if self.racks: + for r in self.racks: + if r.get_id() == rack_key: + return r raise errors.DesignError( "Rack %s not found in design state" % rack_key) @@ -258,9 +261,10 @@ class SiteDesign(base.DrydockPersistentObject, base.DrydockObject): :param ba_key: Value should match the ``get_id()`` value of the BootAction returned """ - for ba in self.bootactions: - if ba.get_id() == ba_key: - return ba + if self.bootactions: + for ba in self.bootactions: + if ba.get_id() == ba_key: + return ba raise errors.DesignError( "BootAction %s not found in design state" % ba_key) @@ -274,9 +278,10 @@ class SiteDesign(base.DrydockPersistentObject, base.DrydockObject): self.host_profiles.append(new_host_profile) def get_host_profile(self, profile_key): - for p in self.host_profiles: - if p.get_id() == profile_key: - return p + if self.host_profiles: + for p in self.host_profiles: + if p.get_id() == profile_key: + return p raise errors.DesignError( "HostProfile %s not found in design state" % profile_key) @@ -291,9 +296,10 @@ class SiteDesign(base.DrydockPersistentObject, base.DrydockObject): self.hardware_profiles.append(new_hardware_profile) def get_hardware_profile(self, profile_key): - for p in self.hardware_profiles: - if p.get_id() == profile_key: - return p + if self.hardware_profiles: + for p in self.hardware_profiles: + if p.get_id() == profile_key: + return p raise errors.DesignError( "HardwareProfile %s not found in design state" % profile_key) @@ -308,9 +314,10 @@ class SiteDesign(base.DrydockPersistentObject, base.DrydockObject): self.baremetal_nodes.append(new_baremetal_node) def get_baremetal_node(self, node_key): - for n in self.baremetal_nodes: - if n.get_id() == node_key: - return n + if self.baremetal_nodes: + for n in self.baremetal_nodes: + if n.get_id() == node_key: + return n raise errors.DesignError( "BaremetalNode %s not found in design state" % node_key) diff --git a/drydock_provisioner/orchestrator/validations/hostname_validity.py b/drydock_provisioner/orchestrator/validations/hostname_validity.py index b671b412..12823d81 100644 --- a/drydock_provisioner/orchestrator/validations/hostname_validity.py +++ b/drydock_provisioner/orchestrator/validations/hostname_validity.py @@ -11,6 +11,8 @@ # 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. +import re + from drydock_provisioner.orchestrator.validations.validators import Validators @@ -19,14 +21,32 @@ class HostnameValidity(Validators): super().__init__('Hostname Validity', 'DD3003') def run_validation(self, site_design, orchestrator=None): - """Validate that node hostnames do not contain '__' """ - node_list = site_design.baremetal_nodes or [] + # Check FQDN length is <= 255 characters per RFC 1035 - invalid_nodes = [n for n in node_list if '__' in n.name] + node_list = site_design.baremetal_nodes or [] + invalid_nodes = [ + n for n in node_list if len(n.get_fqdn(site_design)) > 255 + ] for n in invalid_nodes: - msg = "Hostname %s invalid." % n.name + msg = "FQDN %s is invalid, greater than 255 characters." % n.get_fqdn( + site_design) self.report_error( msg, [n.doc_ref], - "Hostnames cannot contain '__' (double underscore)") - return + "RFC 1035 requires full DNS names to be < 256 characters.") + + # Check each label in the domain name is <= 63 characters per RFC 1035 + # and only contains A-Z,a-z,0-9,- + + valid_label = re.compile('[a-z0-9-]{1,63}', flags=re.I) + + for n in node_list: + domain_labels = n.get_fqdn(site_design).split('.') + for l in domain_labels: + if not valid_label.fullmatch(l): + msg = "FQDN %s is invalid - label '%s' is invalid." % ( + n.get_fqdn(site_design), l) + self.report_error( + msg, [n.doc_ref], + "RFC 1035 requires each label in a DNS name to be <= 63 characters and contain " + "only A-Z, a-z, 0-9, and hyphens.") diff --git a/tests/unit/test_validation_rule_hostname_validity.py b/tests/unit/test_validation_rule_hostname_validity.py index 19d2c0b0..f05be8cc 100644 --- a/tests/unit/test_validation_rule_hostname_validity.py +++ b/tests/unit/test_validation_rule_hostname_validity.py @@ -54,11 +54,13 @@ class TestHostnameValidity(object): validator = HostnameValidity() message_list = validator.execute(site_design, orchestrator=orch) + long_label = "sitenameisverylongsoitshouldbeinvalidperrfcifikeepaddingoctetsafewmore" for msg in message_list: msg = msg.to_dict() LOG.debug(msg) assert msg.get('error') assert len(msg.get('documents')) > 0 - assert "bad__name" in msg.get('message') + assert "bad__name" in msg.get('message') or long_label in msg.get( + 'message') - assert len(message_list) == 1 + assert len(message_list) > 1 diff --git a/tests/yaml_samples/invalid_validation.yaml b/tests/yaml_samples/invalid_validation.yaml index 6c836296..37d27b27 100644 --- a/tests/yaml_samples/invalid_validation.yaml +++ b/tests/yaml_samples/invalid_validation.yaml @@ -182,7 +182,7 @@ data: gateway: 141.16.1.1 metric: 10 dns: - domain: mgmt.sitename.example.com + domain: mgmt.sitenameisverylongsoitshouldbeinvalidperrfcifikeepaddingoctetsafewmore.example.com servers: 172.16.1.9,172.16.1.10 --- schema: 'drydock/Network/v1'