Implement user ssh key injection for deployed nodes
Add authorized_keys field to Site object Add sshkey to maasdriver models Add ConfigureUserCredentials task Add orchestrator step for ConfigureUserCredentials Add driver logic to implement ConfigureUserCredentials
This commit is contained in:
parent
2f7b944903
commit
a27d003b5d
@ -80,6 +80,7 @@ class DrydockConfig(object):
|
||||
timeout_options = [
|
||||
cfg.IntOpt('drydock_timeout', default=5, help='Fallback timeout when a specific one is not configured'),
|
||||
cfg.IntOpt('create_network_template', default=2, help='Timeout in minutes for creating site network templates'),
|
||||
cfg.IntOpt('configure_user_credentials', default=2, help='Timeout in minutes for creating user credentials'),
|
||||
cfg.IntOpt('identify_node', default=10, help='Timeout in minutes for initial node identification'),
|
||||
cfg.IntOpt('configure_hardware', default=30, help='Timeout in minutes for node commissioning and hardware configuration'),
|
||||
cfg.IntOpt('apply_node_networking', default=5, help='Timeout in minutes for configuring node networking'),
|
||||
|
@ -39,7 +39,8 @@ class NodeDriver(ProviderDriver):
|
||||
hd_fields.OrchestratorAction.ApplyNodeStorage,
|
||||
hd_fields.OrchestratorAction.ApplyNodePlatform,
|
||||
hd_fields.OrchestratorAction.DeployNode,
|
||||
hd_fields.OrchestratorAction.DestroyNode]
|
||||
hd_fields.OrchestratorAction.DestroyNode,
|
||||
hd_fields.OrchestratorAction.ConfigureUserCredentials]
|
||||
|
||||
def execute_task(self, task_id):
|
||||
task = self.state_manager.get_task(task_id)
|
||||
|
@ -33,6 +33,7 @@ import drydock_provisioner.drivers.node.maasdriver.models.vlan as maas_vlan
|
||||
import drydock_provisioner.drivers.node.maasdriver.models.subnet as maas_subnet
|
||||
import drydock_provisioner.drivers.node.maasdriver.models.machine as maas_machine
|
||||
import drydock_provisioner.drivers.node.maasdriver.models.tag as maas_tag
|
||||
import drydock_provisioner.drivers.node.maasdriver.models.sshkey as maas_keys
|
||||
|
||||
class MaasNodeDriver(NodeDriver):
|
||||
maasdriver_options = [
|
||||
@ -156,6 +157,41 @@ class MaasNodeDriver(NodeDriver):
|
||||
status=hd_fields.TaskStatus.Complete,
|
||||
result=subtask.get_result())
|
||||
|
||||
return
|
||||
elif task.action == hd_fields.OrchestratorAction.ConfigureUserCredentials:
|
||||
self.orchestrator.task_field_update(task.get_id(), status=hd_fields.TaskStatus.Running)
|
||||
|
||||
subtask = self.orchestrator.create_task(task_model.DriverTask,
|
||||
parent_task_id=task.get_id(), design_id=design_id,
|
||||
action=task.action, site_name=task.site_name,
|
||||
task_scope={'site': task.site_name})
|
||||
runner = MaasTaskRunner(state_manager=self.state_manager,
|
||||
orchestrator=self.orchestrator,
|
||||
task_id=subtask.get_id())
|
||||
|
||||
self.logger.info("Starting thread for task %s to configure user credentials" % (subtask.get_id()))
|
||||
|
||||
runner.start()
|
||||
|
||||
runner.join(timeout=config.conf.timeouts.configure_user_credentials * 60)
|
||||
|
||||
if runner.is_alive():
|
||||
result = {
|
||||
'retry': False,
|
||||
'detail': 'MaaS ssh key creation timed-out'
|
||||
}
|
||||
self.logger.warning("Thread for task %s timed out after 120s" % (subtask.get_id()))
|
||||
self.orchestrator.task_field_update(task.get_id(),
|
||||
status=hd_fields.TaskStatus.Complete,
|
||||
result=hd_fields.ActionResult.Failure,
|
||||
result_detail=result)
|
||||
else:
|
||||
subtask = self.state_manager.get_task(subtask.get_id())
|
||||
self.logger.info("Thread for task %s completed - result %s" % (subtask.get_id(), subtask.get_result()))
|
||||
self.orchestrator.task_field_update(task.get_id(),
|
||||
status=hd_fields.TaskStatus.Complete,
|
||||
result=subtask.get_result())
|
||||
|
||||
return
|
||||
elif task.action == hd_fields.OrchestratorAction.IdentifyNode:
|
||||
self.orchestrator.task_field_update(task.get_id(),
|
||||
@ -762,6 +798,50 @@ class MaasTaskRunner(drivers.DriverTaskRunner):
|
||||
status=hd_fields.TaskStatus.Complete,
|
||||
result=action_result,
|
||||
result_detail=result_detail)
|
||||
elif task_action == hd_fields.OrchestratorAction.ConfigureUserCredentials:
|
||||
try:
|
||||
key_list = maas_keys.SshKeys(self.maas_client)
|
||||
key_list.refresh()
|
||||
except:
|
||||
self.orchestrator.task_field_update(self.task.get_id(),
|
||||
status=hd_fields.TaskStatus.Complete,
|
||||
result=hd_fields.ActionResult.Failure,
|
||||
result_detail={'detail': 'Error accessing MaaS SshKeys API', 'retry': True})
|
||||
return
|
||||
|
||||
site_model = site_design.get_site()
|
||||
|
||||
result_detail = { 'detail': [] }
|
||||
failed = worked = False
|
||||
for k in getattr(site_model, 'authorized_keys', []):
|
||||
try:
|
||||
if len(key_list.query({'key': k.replace("\n","")})) == 0:
|
||||
new_key = maas_keys.SshKey(self.maas_client, key=k)
|
||||
new_key = key_list.add(new_key)
|
||||
self.logger.debug("Added SSH key %s to MaaS user profile. Will be installed on all deployed nodes." %
|
||||
(k[:16]))
|
||||
result_detail['detail'].append("Added SSH key %s" % (k[:16]))
|
||||
worked = True
|
||||
else:
|
||||
self.logger.debug("SSH key %s already exists in MaaS user profile." % k[:16])
|
||||
result_detail['detail'].append("SSH key %s alreayd exists" % (k[:16]))
|
||||
worked = True
|
||||
except Exception as ex:
|
||||
self.logger.warning("Error adding SSH key to MaaS user profile: %s" % str(ex))
|
||||
result_detail['detail'].append("Failed to add SSH key %s" % (k[:16]))
|
||||
failed = True
|
||||
|
||||
if worked and failed:
|
||||
final_result = hd_fields.ActionResult.PartialSuccess
|
||||
elif worked:
|
||||
final_result = hd_fields.ActionResult.Success
|
||||
else:
|
||||
final_result = hd_fields.ActionResult.Failure
|
||||
|
||||
self.orchestrator.task_field_update(self.task.get_id(),
|
||||
status=hd_fields.TaskStatus.Complete,
|
||||
result=final_result, result_detail=result_detail)
|
||||
return
|
||||
elif task_action == hd_fields.OrchestratorAction.IdentifyNode:
|
||||
try:
|
||||
machine_list = maas_machine.Machines(self.maas_client)
|
||||
|
@ -197,7 +197,7 @@ class ResourceCollectionBase(object):
|
||||
|
||||
resp = self.api_client.post(url, files=data_dict)
|
||||
|
||||
if resp.status_code == 200:
|
||||
if resp.status_code in [200,201]:
|
||||
resp_json = resp.json()
|
||||
res.set_resource_id(resp_json.get('id'))
|
||||
return res
|
||||
|
@ -113,6 +113,10 @@ class YamlIngester(IngesterPlugin):
|
||||
'NodeTagDefinition: %s' % (self.definition_type))
|
||||
model.tag_definitions.append(tag_model)
|
||||
|
||||
auth_keys = spec.get('authorized_keys', [])
|
||||
|
||||
model.authorized_keys = [k for k in auth_keys]
|
||||
|
||||
models.append(model)
|
||||
else:
|
||||
raise ValueError('Unknown API version %s of Region kind' %s (api_version))
|
||||
@ -330,10 +334,8 @@ class YamlIngester(IngesterPlugin):
|
||||
|
||||
node_metadata = spec.get('metadata', {})
|
||||
metadata_tags = node_metadata.get('tags', [])
|
||||
model.tags = []
|
||||
|
||||
for t in metadata_tags:
|
||||
model.tags.append(t)
|
||||
model.tags = [t for t in metadata_tags]
|
||||
|
||||
owner_data = node_metadata.get('owner_data', {})
|
||||
model.owner_data = {}
|
||||
|
@ -43,6 +43,7 @@ class OrchestratorAction(BaseDrydockEnum):
|
||||
CreateNetworkTemplate = 'create_network_template'
|
||||
CreateStorageTemplate = 'create_storage_template'
|
||||
CreateBootMedia = 'create_boot_media'
|
||||
ConfigureUserCredentials = 'configure_user_credentials'
|
||||
PrepareHardwareConfig = 'prepare_hardware_config'
|
||||
IdentifyNode = 'identify_node'
|
||||
ConfigureHardware = 'configure_hardware'
|
||||
|
@ -37,6 +37,7 @@ class Site(base.DrydockPersistentObject, base.DrydockObject):
|
||||
'tag_definitions': ovo_fields.ObjectField('NodeTagDefinitionList',
|
||||
nullable=True),
|
||||
'repositories': ovo_fields.ObjectField('RepositoryList', nullable=True),
|
||||
'authorized_keys': ovo_fields.ListOfStringsField(nullable=True),
|
||||
}
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
@ -51,6 +52,9 @@ class Site(base.DrydockPersistentObject, base.DrydockObject):
|
||||
def add_tag_definition(self, tag_definition):
|
||||
self.tag_definitions.append(tag_definition)
|
||||
|
||||
def add_key(self, key_string):
|
||||
self.authorized_keys.append(key_string)
|
||||
|
||||
@base.DrydockObjectRegistry.register
|
||||
class NodeTagDefinition(base.DrydockObject):
|
||||
|
||||
|
@ -162,23 +162,60 @@ class Orchestrator(object):
|
||||
'site': task.site
|
||||
}
|
||||
|
||||
driver_task = self.create_task(tasks.DriverTask,
|
||||
worked = failed = False
|
||||
|
||||
site_network_task = self.create_task(tasks.DriverTask,
|
||||
parent_task_id=task.get_id(),
|
||||
design_id=design_id,
|
||||
task_scope=task_scope,
|
||||
action=hd_fields.OrchestratorAction.CreateNetworkTemplate)
|
||||
|
||||
self.logger.info("Starting node driver task %s to create network templates" % (driver_task.get_id()))
|
||||
self.logger.info("Starting node driver task %s to create network templates" % (site_network_task.get_id()))
|
||||
|
||||
driver.execute_task(driver_task.get_id())
|
||||
driver.execute_task(site_network_task.get_id())
|
||||
|
||||
driver_task = self.state_manager.get_task(driver_task.get_id())
|
||||
site_network_task = self.state_manager.get_task(site_network_task.get_id())
|
||||
|
||||
self.logger.info("Node driver task %s complete" % (driver_task.get_id()))
|
||||
if site_network_task.get_result() in [hd_fields.ActionResult.Success,
|
||||
hd_fields.ActionResult.PartialSuccess]:
|
||||
worked = True
|
||||
if site_network_task.get_result() in [hd_fields.ActionResult.Failure,
|
||||
hd_fields.ActionResult.PartialSuccess]:
|
||||
failed = True
|
||||
|
||||
self.logger.info("Node driver task %s complete" % (site_network_task.get_id()))
|
||||
|
||||
user_creds_task = self.create_task(tasks.DriverTask,
|
||||
parent_task_id=task.get_id(),
|
||||
design_id=design_id,
|
||||
task_scope=task_scope,
|
||||
action=hd_fields.OrchestratorAction.ConfigureUserCredentials)
|
||||
|
||||
self.logger.info("Starting node driver task %s to configure user credentials" % (user_creds_task.get_id()))
|
||||
|
||||
driver.execute_task(user_creds_task.get_id())
|
||||
|
||||
self.logger.info("Node driver task %s complete" % (site_network_task.get_id()))
|
||||
|
||||
user_creds_task = self.state_manager.get_task(site_network_task.get_id())
|
||||
|
||||
if user_creds_task.get_result() in [hd_fields.ActionResult.Success,
|
||||
hd_fields.ActionResult.PartialSuccess]:
|
||||
worked = True
|
||||
if user_creds_task.get_result() in [hd_fields.ActionResult.Failure,
|
||||
hd_fields.ActionResult.PartialSuccess]:
|
||||
failed = True
|
||||
|
||||
if worked and failed:
|
||||
final_result = hd_fields.ActionResult.PartialSuccess
|
||||
elif worked:
|
||||
final_result = hd_fields.ActionResult.Success
|
||||
else:
|
||||
final_result = hd_fields.ActionResult.Failure
|
||||
|
||||
self.task_field_update(task_id,
|
||||
status=hd_fields.TaskStatus.Complete,
|
||||
result=driver_task.get_result())
|
||||
result=final_result)
|
||||
return
|
||||
elif task.action == hd_fields.OrchestratorAction.VerifyNode:
|
||||
self.task_field_update(task_id,
|
||||
|
@ -33,6 +33,11 @@ spec:
|
||||
# Image and package repositories needed by Drydock drivers. Needs to be defined
|
||||
repositories:
|
||||
- name: 'ubuntu-main'
|
||||
authorized_keys:
|
||||
- |
|
||||
valid ssh key string
|
||||
- |
|
||||
valid ssh key string
|
||||
---
|
||||
apiVersion: 'v1.0'
|
||||
kind: NetworkLink
|
||||
|
Loading…
Reference in New Issue
Block a user