fuel-astute/lib/astute/deployment_engine/granular_deployment.rb
Georgy Kibardin 668287af1c Puppet debug is turned on via puppet_debug
Do not lose the puppet debug flag.

Change-Id: I697b92714f555e406de7214329c287435f26e0c4
Related-Bug: #1592842
Related-Bug: #1560505
2016-07-04 13:29:15 +00:00

272 lines
8.5 KiB
Ruby

# Copyright 2014 Mirantis, Inc.
#
# 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.
class Astute::DeploymentEngine::GranularDeployment < Astute::DeploymentEngine
NAILGUN_STATUS = ['ready', 'error', 'deploying']
def deploy_piece(nodes, retries=1)
report_ready_for_nodes_without_tasks(nodes)
nodes = filter_nodes_with_tasks(nodes)
return false unless validate_nodes(nodes)
@ctx.reporter.report(nodes_status(nodes, 'deploying', {'progress' => 0}))
log_preparation(nodes)
Astute.logger.info "#{@ctx.task_id}: Starting deployment"
@running_tasks = {}
@start_times = {}
@nodes_roles = nodes.inject({}) { |h, n| h.merge({n['uid'] => n['role']}) }
@nodes_by_uid = nodes.inject({}) { |h, n| h.merge({ n['uid'] => n }) }
@puppet_debug = nodes.first.fetch('puppet_debug', true)
begin
@task_manager = Astute::TaskManager.new(nodes)
@hook_context = Astute::Context.new(
@ctx.task_id,
HookReporter.new,
Astute::LogParser::NoParsing.new
)
deploy_nodes(nodes)
rescue => e
# We should fail all nodes in case of post deployment
# process. In other case they will not sending back
# for redeploy
report_nodes = nodes.uniq{ |n| n['uid'] }.map do |node|
{ 'uid' => node['uid'],
'status' => 'error',
'role' => node['role'],
'error_type' => 'deploy'
}
end
@ctx.report_and_update_status('nodes' => report_nodes)
raise e
end
Astute.logger.info "#{@ctx.task_id}: Finished deployment of nodes" \
" => roles: #{@nodes_roles.pretty_inspect}"
end
def puppet_task(node_id, task)
# Use fake reporter because of logic. We need to handle report here
Astute::PuppetTask.new(
@hook_context,
@nodes_by_uid[node_id], # Use single node uid instead of task['uids']
{
:retries => task['parameters']['retries'],
:puppet_manifest => task['parameters']['puppet_manifest'],
:puppet_modules => task['parameters']['puppet_modules'],
:cwd => task['parameters']['cwd'],
:timeout => task['parameters']['timeout'],
:puppet_debug => @puppet_debug
}
)
end
def run_task(node_id, task)
@start_times[node_id] = {
'time_start' => Time.now.to_i,
'task_name' => task_name(task)
}
Astute.logger.info "#{@ctx.task_id}: run task '#{task.to_yaml}' on " \
"node #{node_id}"
@running_tasks[node_id] = puppet_task(node_id, task)
@running_tasks[node_id].run
end
def check_status(node_id)
status = @running_tasks[node_id].status
if NAILGUN_STATUS.include? status
status
else
raise "Internal error. Unknown status '#{status}'"
end
end
def deploy_nodes(nodes)
@task_manager.node_uids.each { |n| task = @task_manager.next_task(n) and run_task(n, task) }
while @task_manager.task_in_queue?
nodes_to_report = []
sleep Astute.config.puppet_deploy_interval
@task_manager.node_uids.each do |node_id|
if task = @task_manager.current_task(node_id)
case status = check_status(node_id)
when 'ready'
Astute.logger.info "Task '#{task}' on node uid=#{node_id} " \
"ended successfully"
time_summary(node_id, status)
new_task = @task_manager.next_task(node_id)
if new_task
run_task(node_id, new_task)
else
nodes_to_report << process_success_node(node_id, task)
end
when 'deploying'
progress_report = process_running_node(node_id, task, nodes)
nodes_to_report << progress_report if progress_report
when 'error'
Astute.logger.error "Task '#{task}' failed on node #{node_id}"
nodes_to_report << process_fail_node(node_id, task)
time_summary(node_id, status)
else
raise "Internal error. Known status '#{status}', but " \
"handler not provided"
end
else
Astute.logger.debug "No more tasks provided for node #{node_id}"
end
end
@ctx.report_and_update_status('nodes' => nodes_to_report) if nodes_to_report.present?
break unless @task_manager.task_in_queue?
end
end
def process_success_node(node_id, task)
Astute.logger.info "No more tasks provided for node #{node_id}. All node " \
"tasks completed successfully"
{
"uid" => node_id,
'status' => 'ready',
'role' => @nodes_roles[node_id],
"progress" => 100,
'task' => task
}
end
def process_fail_node(node_id, task)
Astute.logger.error "No more tasks will be executed on the node #{node_id}"
@task_manager.delete_node(node_id)
{
'uid' => node_id,
'status' => 'error',
'error_type' => 'deploy',
'role' => @nodes_roles[node_id],
'task' => task
}
end
def process_running_node(node_id, task, nodes)
Astute.logger.debug "Task '#{task}' on node uid=#{node_id} deploying"
begin
# Pass nodes because logs calculation needs IP address of node, not just uid
nodes_progress = @ctx.deploy_log_parser.progress_calculate(Array(node_id), nodes)
if nodes_progress.present?
nodes_progress.map! { |x| x.merge!(
'status' => 'deploying',
'role' => @nodes_roles[x['uid']],
'task' => task
) }
nodes_progress.first
else
nil
end
rescue => e
Astute.logger.warn "Some error occurred when parse logs for nodes progress: #{e.message}, "\
"trace: #{e.format_backtrace}"
nil
end
end
def log_preparation(nodes)
@ctx.deploy_log_parser.prepare(nodes)
rescue => e
Astute.logger.warn "Some error occurred when prepare LogParser: " \
"#{e.message}, trace: #{e.format_backtrace}"
end
# If node doesn't have tasks, it means that node
# is ready, because it doesn't require deployment
def report_ready_for_nodes_without_tasks(nodes)
nodes_without_tasks = filter_nodes_without_tasks(nodes)
@ctx.reporter.report(nodes_status(nodes_without_tasks, 'ready', {'progress' => 100}))
end
def filter_nodes_with_tasks(nodes)
nodes.select { |n| node_with_tasks?(n) }
end
def filter_nodes_without_tasks(nodes)
nodes.select { |n| !node_with_tasks?(n) }
end
def node_with_tasks?(node)
node['tasks'].present?
end
# Pre/post hooks
def pre_deployment_actions(deployment_info, pre_deployment)
Astute::GranularPreDeploymentActions.new(deployment_info, @ctx).process
Astute::NailgunHooks.new(pre_deployment, @ctx).process
end
def pre_node_actions(part)
@action ||= Astute::GranularPreNodeActions.new(@ctx)
@action.process(part)
end
def pre_deploy_actions(part)
Astute::GranularPreDeployActions.new(part, @ctx).process
end
def post_deploy_actions(part)
Astute::GranularPostDeployActions.new(part, @ctx).process
end
def post_deployment_actions(deployment_info, post_deployment)
begin
Astute::NailgunHooks.new(post_deployment, @ctx).process
rescue => e
# We should fail all nodes in case of post deployment
# process. In other case they will not sending back
# for redeploy
nodes = deployment_info.uniq {|n| n['uid']}.map do |node|
{ 'uid' => node['uid'],
'status' => 'error',
'role' => 'hook',
'error_type' => 'deploy',
}
end
@ctx.report_and_update_status('nodes' => nodes)
raise e
end
end
def time_summary(node_id, status)
return unless @start_times.fetch(node_id, {}).fetch('time_start', nil)
amount_time = (Time.now.to_i - @start_times[node_id]['time_start']).to_i
wasted_time = Time.at(amount_time).utc.strftime("%H:%M:%S")
Astute.logger.debug("Task time summary:" \
" #{@start_times[node_id]['task_name']} with status" \
" #{status.to_s} on node #{node_id} took #{wasted_time}")
end
def task_name(task)
task['id'] || task['diagnostic_name'] || task['type']
end
class HookReporter
def report(msg)
Astute.logger.debug msg
end
end
end