668287af1c
Do not lose the puppet debug flag. Change-Id: I697b92714f555e406de7214329c287435f26e0c4 Related-Bug: #1592842 Related-Bug: #1560505
272 lines
8.5 KiB
Ruby
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
|