[373577] Fix hostnames with underscores

Hostnames with underscores caused a deployment failure.

Update to use a double underscore as a delimiter and provide
a design validator to check that hostnames do not contain it.

Closes #78

Change-Id: Ib148aed5cffe7fd8bc08441eaef8a45af6601bdd
This commit is contained in:
Scott Hussey 2018-04-27 11:35:33 -05:00
parent b138b3c179
commit 3b41868802
8 changed files with 112 additions and 7 deletions

View File

@ -227,6 +227,7 @@ reference to the particular physical node. The ``BaremetalNode`` definition will
reference a ``HostProfile`` and can then extend or override any of the
configuration values.
NOTE: Drydock does not support hostnames containing '__' (double underscoe)
Hardware Profile
----------------

View File

@ -660,7 +660,9 @@ class ConfigureUserCredentials(BaseMaasAction):
if key_list:
for k in key_list:
try:
if len(current_keys.query({'key': k.replace("\n", "")})) == 0:
if len(current_keys.query({
'key': k.replace("\n", "")
})) == 0:
new_key = maas_keys.SshKey(self.maas_client, key=k)
new_key = current_keys.add(new_key)
msg = "Added SSH key %s to MaaS user profile. Will be installed on all deployed nodes." % (
@ -1814,7 +1816,8 @@ class ApplyNodeStorage(BaseMaasAction):
raise errors.NotEnoughStorage()
if match.group(1) == '>':
computed_size = int(context.available_size) - ApplyNodeStorage.PART_TABLE_RESERVATION
computed_size = int(context.available_size
) - ApplyNodeStorage.PART_TABLE_RESERVATION
return computed_size
@ -1919,7 +1922,7 @@ class DeployNode(BaseMaasAction):
tag_list = maas_tag.Tags(self.maas_client)
tag_list.refresh()
node_id_tags = tag_list.startswith("%s_baid-" % (n.name))
node_id_tags = tag_list.startswith("%s__baid__" % (n.name))
for t in node_id_tags:
t.delete()
@ -1929,7 +1932,7 @@ class DeployNode(BaseMaasAction):
self.logger.debug(msg)
node_baid_tag = maas_tag.Tag(
self.maas_client,
name="%s_baid-%s" % (n.name, ba_key.hex()))
name="%s__baid__%s" % (n.name, ba_key.hex()))
node_baid_tag = tag_list.add(node_baid_tag)
node_baid_tag.apply_to_node(machine.resource_id)
self.task.add_status_msg(

View File

@ -24,6 +24,7 @@ from bson import BSON
LOG = logging.getLogger(__name__)
class Machine(model_base.ResourceBase):
resource_url = 'machines/{resource_id}/'

View File

@ -0,0 +1,32 @@
# 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.
from drydock_provisioner.orchestrator.validations.validators import Validators
class HostnameValidity(Validators):
def __init__(self):
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 []
invalid_nodes = [n for n in node_list if '__' in n.name]
for n in invalid_nodes:
msg = "Hostname %s invalid." % n.name
self.report_error(
msg, [n.doc_ref],
"Hostnames cannot contain '__' (double underscore)")
return

View File

@ -27,6 +27,7 @@ from drydock_provisioner.orchestrator.validations.rational_network_bond import R
from drydock_provisioner.orchestrator.validations.storage_partititioning import StoragePartitioning
from drydock_provisioner.orchestrator.validations.storage_sizing import StorageSizing
from drydock_provisioner.orchestrator.validations.unique_network_check import UniqueNetworkCheck
from drydock_provisioner.orchestrator.validations.hostname_validity import HostnameValidity
class Validator():
@ -86,4 +87,5 @@ rule_set = [
StoragePartitioning(),
StorageSizing(),
UniqueNetworkCheck(),
HostnameValidity(),
]

View File

@ -282,8 +282,8 @@ class DrydockState(object):
"""
try:
conn = self.db_engine.connect()
query = self.tasks_tbl.insert().values(**(
task.to_db(include_id=True)))
query = self.tasks_tbl.insert().values(
**(task.to_db(include_id=True)))
conn.execute(query)
conn.close()
return True

View File

@ -0,0 +1,64 @@
# Copyright 2017 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 Validation Rule Hostname Validity"""
import logging
from drydock_provisioner.orchestrator.orchestrator import Orchestrator
from drydock_provisioner.orchestrator.validations.hostname_validity import HostnameValidity
LOG = logging.getLogger(__name__)
class TestHostnameValidity(object):
def test_hostname(self, mocker, deckhand_ingester, drydock_state,
input_files):
input_file = input_files.join("validation.yaml")
design_ref = "file://%s" % str(input_file)
orch = Orchestrator(
state_manager=drydock_state, ingester=deckhand_ingester)
status, site_design = Orchestrator.get_effective_site(orch, design_ref)
validator = HostnameValidity()
message_list = validator.execute(site_design, orchestrator=orch)
msg = message_list[0].to_dict()
assert 'Hostname' in msg.get('message')
assert msg.get('error') is False
assert len(message_list) == 1
def test_invalid_hostname(self, mocker, deckhand_ingester, drydock_state,
input_files):
input_file = input_files.join("invalid_validation.yaml")
design_ref = "file://%s" % str(input_file)
orch = Orchestrator(
state_manager=drydock_state, ingester=deckhand_ingester)
status, site_design = Orchestrator.get_effective_site(orch, design_ref)
validator = HostnameValidity()
message_list = validator.execute(site_design, orchestrator=orch)
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 len(message_list) == 1

View File

@ -358,7 +358,9 @@ data:
schema: 'drydock/BaremetalNode/v1'
metadata:
schema: 'metadata/Document/v1'
name: compute01
#####
# Invalid hostname contains '__'
name: bad__name
storagePolicy: 'cleartext'
labels:
application: 'drydock'