Node naming validations

- Update the hostname validator account for the FQDN

Change-Id: If59eb5d5d351e2251cf881492b46e109111c91ec
This commit is contained in:
Scott Hussey 2018-08-03 12:20:02 -05:00
parent 89ce941202
commit e044575e05
4 changed files with 59 additions and 30 deletions

View File

@ -203,9 +203,10 @@ class SiteDesign(base.DrydockPersistentObject, base.DrydockObject):
self.networks.append(new_network) self.networks.append(new_network)
def get_network(self, network_key): def get_network(self, network_key):
for n in self.networks: if self.networks:
if n.get_id() == network_key: for n in self.networks:
return n if n.get_id() == network_key:
return n
raise errors.DesignError( raise errors.DesignError(
"Network %s not found in design state" % network_key) "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) self.network_links.append(new_network_link)
def get_network_link(self, link_key): def get_network_link(self, link_key):
for l in self.network_links: if self.network_links:
if l.get_id() == link_key: for l in self.network_links:
return l if l.get_id() == link_key:
return l
raise errors.DesignError( raise errors.DesignError(
"NetworkLink %s not found in design state" % link_key) "NetworkLink %s not found in design state" % link_key)
@ -237,9 +239,10 @@ class SiteDesign(base.DrydockPersistentObject, base.DrydockObject):
self.racks.append(new_rack) self.racks.append(new_rack)
def get_rack(self, rack_key): def get_rack(self, rack_key):
for r in self.racks: if self.racks:
if r.get_id() == rack_key: for r in self.racks:
return r if r.get_id() == rack_key:
return r
raise errors.DesignError( raise errors.DesignError(
"Rack %s not found in design state" % rack_key) "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 :param ba_key: Value should match the ``get_id()`` value of the BootAction returned
""" """
for ba in self.bootactions: if self.bootactions:
if ba.get_id() == ba_key: for ba in self.bootactions:
return ba if ba.get_id() == ba_key:
return ba
raise errors.DesignError( raise errors.DesignError(
"BootAction %s not found in design state" % ba_key) "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) self.host_profiles.append(new_host_profile)
def get_host_profile(self, profile_key): def get_host_profile(self, profile_key):
for p in self.host_profiles: if self.host_profiles:
if p.get_id() == profile_key: for p in self.host_profiles:
return p if p.get_id() == profile_key:
return p
raise errors.DesignError( raise errors.DesignError(
"HostProfile %s not found in design state" % profile_key) "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) self.hardware_profiles.append(new_hardware_profile)
def get_hardware_profile(self, profile_key): def get_hardware_profile(self, profile_key):
for p in self.hardware_profiles: if self.hardware_profiles:
if p.get_id() == profile_key: for p in self.hardware_profiles:
return p if p.get_id() == profile_key:
return p
raise errors.DesignError( raise errors.DesignError(
"HardwareProfile %s not found in design state" % profile_key) "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) self.baremetal_nodes.append(new_baremetal_node)
def get_baremetal_node(self, node_key): def get_baremetal_node(self, node_key):
for n in self.baremetal_nodes: if self.baremetal_nodes:
if n.get_id() == node_key: for n in self.baremetal_nodes:
return n if n.get_id() == node_key:
return n
raise errors.DesignError( raise errors.DesignError(
"BaremetalNode %s not found in design state" % node_key) "BaremetalNode %s not found in design state" % node_key)

View File

@ -11,6 +11,8 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
import re
from drydock_provisioner.orchestrator.validations.validators import Validators from drydock_provisioner.orchestrator.validations.validators import Validators
@ -19,14 +21,32 @@ class HostnameValidity(Validators):
super().__init__('Hostname Validity', 'DD3003') super().__init__('Hostname Validity', 'DD3003')
def run_validation(self, site_design, orchestrator=None): def run_validation(self, site_design, orchestrator=None):
"""Validate that node hostnames do not contain '__' """ # Check FQDN length is <= 255 characters per RFC 1035
node_list = site_design.baremetal_nodes or []
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: 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( self.report_error(
msg, [n.doc_ref], msg, [n.doc_ref],
"Hostnames cannot contain '__' (double underscore)") "RFC 1035 requires full DNS names to be < 256 characters.")
return
# 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.")

View File

@ -54,11 +54,13 @@ class TestHostnameValidity(object):
validator = HostnameValidity() validator = HostnameValidity()
message_list = validator.execute(site_design, orchestrator=orch) message_list = validator.execute(site_design, orchestrator=orch)
long_label = "sitenameisverylongsoitshouldbeinvalidperrfcifikeepaddingoctetsafewmore"
for msg in message_list: for msg in message_list:
msg = msg.to_dict() msg = msg.to_dict()
LOG.debug(msg) LOG.debug(msg)
assert msg.get('error') assert msg.get('error')
assert len(msg.get('documents')) > 0 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

View File

@ -182,7 +182,7 @@ data:
gateway: 141.16.1.1 gateway: 141.16.1.1
metric: 10 metric: 10
dns: dns:
domain: mgmt.sitename.example.com domain: mgmt.sitenameisverylongsoitshouldbeinvalidperrfcifikeepaddingoctetsafewmore.example.com
servers: 172.16.1.9,172.16.1.10 servers: 172.16.1.9,172.16.1.10
--- ---
schema: 'drydock/Network/v1' schema: 'drydock/Network/v1'