
Adding the baclient code to Drydock requires a refactor of the build automation to support multiple languages and multiple artifacts included in a single Docker image NOTE: the go source here is a placeholder 'hello world' sample Change-Id: I1b4883f018b33b3d4fcd7cbcb6cba660fcdc93de
252 lines
9.6 KiB
Python
252 lines
9.6 KiB
Python
# 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.
|
|
"""Task driver for completing node provisioning with Canonical MaaS 2.2+."""
|
|
|
|
import logging
|
|
import uuid
|
|
import concurrent.futures
|
|
|
|
from oslo_config import cfg
|
|
|
|
import drydock_provisioner.error as errors
|
|
import drydock_provisioner.objects.fields as hd_fields
|
|
import drydock_provisioner.config as config
|
|
|
|
from drydock_provisioner.drivers.node.driver import NodeDriver
|
|
from drydock_provisioner.drivers.node.maasdriver.api_client import MaasRequestFactory
|
|
from drydock_provisioner.drivers.node.maasdriver.models.boot_resource import BootResources
|
|
|
|
from .actions.node import ValidateNodeServices
|
|
from .actions.node import CreateStorageTemplate
|
|
from .actions.node import CreateBootMedia
|
|
from .actions.node import PrepareHardwareConfig
|
|
from .actions.node import InterrogateNode
|
|
from .actions.node import DestroyNode
|
|
from .actions.node import CreateNetworkTemplate
|
|
from .actions.node import ConfigureUserCredentials
|
|
from .actions.node import IdentifyNode
|
|
from .actions.node import ConfigureHardware
|
|
from .actions.node import ApplyNodeNetworking
|
|
from .actions.node import ApplyNodePlatform
|
|
from .actions.node import ApplyNodeStorage
|
|
from .actions.node import DeployNode
|
|
from .actions.node import ConfigureNodeProvisioner
|
|
|
|
|
|
class MaasNodeDriver(NodeDriver):
|
|
maasdriver_options = [
|
|
cfg.StrOpt(
|
|
'maas_api_key', help='The API key for accessing MaaS',
|
|
secret=True),
|
|
cfg.StrOpt('maas_api_url', help='The URL for accessing MaaS API'),
|
|
cfg.IntOpt(
|
|
'poll_interval',
|
|
default=10,
|
|
help='Polling interval for querying MaaS status in seconds'),
|
|
]
|
|
|
|
driver_name = 'maasdriver'
|
|
driver_key = 'maasdriver'
|
|
driver_desc = 'MaaS Node Provisioning Driver'
|
|
|
|
action_class_map = {
|
|
hd_fields.OrchestratorAction.ValidateNodeServices:
|
|
ValidateNodeServices,
|
|
hd_fields.OrchestratorAction.CreateStorageTemplate:
|
|
CreateStorageTemplate,
|
|
hd_fields.OrchestratorAction.CreateBootMedia:
|
|
CreateBootMedia,
|
|
hd_fields.OrchestratorAction.PrepareHardwareConfig:
|
|
PrepareHardwareConfig,
|
|
hd_fields.OrchestratorAction.InterrogateNode:
|
|
InterrogateNode,
|
|
hd_fields.OrchestratorAction.DestroyNode:
|
|
DestroyNode,
|
|
hd_fields.OrchestratorAction.CreateNetworkTemplate:
|
|
CreateNetworkTemplate,
|
|
hd_fields.OrchestratorAction.ConfigureUserCredentials:
|
|
ConfigureUserCredentials,
|
|
hd_fields.OrchestratorAction.IdentifyNode:
|
|
IdentifyNode,
|
|
hd_fields.OrchestratorAction.ConfigureHardware:
|
|
ConfigureHardware,
|
|
hd_fields.OrchestratorAction.ApplyNodeNetworking:
|
|
ApplyNodeNetworking,
|
|
hd_fields.OrchestratorAction.ApplyNodePlatform:
|
|
ApplyNodePlatform,
|
|
hd_fields.OrchestratorAction.ApplyNodeStorage:
|
|
ApplyNodeStorage,
|
|
hd_fields.OrchestratorAction.DeployNode:
|
|
DeployNode,
|
|
hd_fields.OrchestratorAction.ConfigureNodeProvisioner:
|
|
ConfigureNodeProvisioner,
|
|
}
|
|
|
|
def __init__(self, **kwargs):
|
|
super().__init__(**kwargs)
|
|
|
|
cfg.CONF.register_opts(
|
|
MaasNodeDriver.maasdriver_options, group=MaasNodeDriver.driver_key)
|
|
|
|
self.logger = logging.getLogger(
|
|
cfg.CONF.logging.nodedriver_logger_name)
|
|
|
|
def execute_task(self, task_id):
|
|
# actions that should be threaded for execution
|
|
threaded_actions = [
|
|
hd_fields.OrchestratorAction.InterrogateNode,
|
|
hd_fields.OrchestratorAction.DestroyNode,
|
|
hd_fields.OrchestratorAction.IdentifyNode,
|
|
hd_fields.OrchestratorAction.ConfigureHardware,
|
|
hd_fields.OrchestratorAction.ApplyNodeNetworking,
|
|
hd_fields.OrchestratorAction.ApplyNodeStorage,
|
|
hd_fields.OrchestratorAction.ApplyNodePlatform,
|
|
hd_fields.OrchestratorAction.DeployNode
|
|
]
|
|
|
|
action_timeouts = {
|
|
hd_fields.OrchestratorAction.IdentifyNode:
|
|
config.config_mgr.conf.timeouts.identify_node,
|
|
hd_fields.OrchestratorAction.ConfigureHardware:
|
|
config.config_mgr.conf.timeouts.configure_hardware,
|
|
hd_fields.OrchestratorAction.DeployNode:
|
|
config.config_mgr.conf.timeouts.deploy_node,
|
|
}
|
|
|
|
task = self.state_manager.get_task(task_id)
|
|
|
|
if task is None:
|
|
raise errors.DriverError("Invalid task %s" % (task_id))
|
|
|
|
if task.action not in self.supported_actions:
|
|
raise errors.DriverError("Driver %s doesn't support task action %s"
|
|
% (self.driver_desc, task.action))
|
|
|
|
task.set_status(hd_fields.TaskStatus.Running)
|
|
task.save()
|
|
|
|
if task.action in threaded_actions:
|
|
if task.retry > 0:
|
|
msg = "Retrying task %s on previous failed entities." % str(
|
|
task.get_id())
|
|
task.add_status_msg(
|
|
msg=msg,
|
|
error=False,
|
|
ctx=str(task.get_id()),
|
|
ctx_type='task')
|
|
target_nodes = self.orchestrator.get_target_nodes(
|
|
task, failures=True)
|
|
else:
|
|
target_nodes = self.orchestrator.get_target_nodes(task)
|
|
|
|
with concurrent.futures.ThreadPoolExecutor(max_workers=16) as e:
|
|
subtask_futures = dict()
|
|
for n in target_nodes:
|
|
maas_client = MaasRequestFactory(
|
|
config.config_mgr.conf.maasdriver.maas_api_url,
|
|
config.config_mgr.conf.maasdriver.maas_api_key)
|
|
nf = self.orchestrator.create_nodefilter_from_nodelist([n])
|
|
subtask = self.orchestrator.create_task(
|
|
design_ref=task.design_ref,
|
|
action=task.action,
|
|
node_filter=nf,
|
|
retry=task.retry)
|
|
task.register_subtask(subtask)
|
|
|
|
action = self.action_class_map.get(task.action, None)(
|
|
subtask,
|
|
self.orchestrator,
|
|
self.state_manager,
|
|
maas_client=maas_client)
|
|
subtask_futures[subtask.get_id().bytes] = e.submit(
|
|
action.start)
|
|
|
|
timeout = action_timeouts.get(
|
|
task.action,
|
|
config.config_mgr.conf.timeouts.drydock_timeout)
|
|
finished, running = concurrent.futures.wait(
|
|
subtask_futures.values(), timeout=(timeout * 60))
|
|
|
|
for t, f in subtask_futures.items():
|
|
if not f.done():
|
|
task.add_status_msg(
|
|
"Subtask %s timed out before completing.",
|
|
error=True,
|
|
ctx=str(uuid.UUID(bytes=t)),
|
|
ctx_type='task')
|
|
task.failure()
|
|
else:
|
|
if f.exception():
|
|
self.logger.error(
|
|
"Uncaught exception in subtask %s." % str(
|
|
uuid.UUID(bytes=t)),
|
|
exc_info=f.exception())
|
|
task.failure()
|
|
task.bubble_results()
|
|
task.align_result()
|
|
else:
|
|
try:
|
|
maas_client = MaasRequestFactory(
|
|
config.config_mgr.conf.maasdriver.maas_api_url,
|
|
config.config_mgr.conf.maasdriver.maas_api_key)
|
|
action = self.action_class_map.get(task.action, None)(
|
|
task,
|
|
self.orchestrator,
|
|
self.state_manager,
|
|
maas_client=maas_client)
|
|
action.start()
|
|
except Exception as e:
|
|
msg = ("Subtask for action %s raised unexpected exception: %s"
|
|
% (task.action, str(e)))
|
|
self.logger.error(msg, exc_info=e)
|
|
task.add_status_msg(
|
|
msg=msg,
|
|
error=True,
|
|
ctx=str(task.get_id()),
|
|
ctx_type='task')
|
|
task.failure()
|
|
|
|
task.set_status(hd_fields.TaskStatus.Complete)
|
|
task.save()
|
|
|
|
return
|
|
|
|
def get_available_images(self):
|
|
"""Return images available in MAAS."""
|
|
maas_client = MaasRequestFactory(
|
|
config.config_mgr.conf.maasdriver.maas_api_url,
|
|
config.config_mgr.conf.maasdriver.maas_api_key)
|
|
|
|
br = BootResources(maas_client)
|
|
br.refresh()
|
|
|
|
return br.get_available_images()
|
|
|
|
def get_available_kernels(self, image_name):
|
|
"""Return kernels available for ``image_name``.
|
|
|
|
:param image_name: str image name (e.g. 'xenial')
|
|
"""
|
|
maas_client = MaasRequestFactory(
|
|
config.config_mgr.conf.maasdriver.maas_api_url,
|
|
config.config_mgr.conf.maasdriver.maas_api_key)
|
|
|
|
br = BootResources(maas_client)
|
|
br.refresh()
|
|
|
|
return br.get_available_kernels(image_name)
|
|
|
|
|
|
def list_opts():
|
|
return {MaasNodeDriver.driver_key: MaasNodeDriver.maasdriver_options}
|