253 lines
11 KiB
Ruby
253 lines
11 KiB
Ruby
require 'json'
|
|
require 'timeout'
|
|
|
|
module Astute
|
|
class DeploymentEngine
|
|
def initialize(context)
|
|
if self.class.superclass.name == 'Object'
|
|
raise "Instantiation of this superclass is not allowed. Please subclass from #{self.class.name}."
|
|
end
|
|
@ctx = context
|
|
end
|
|
|
|
def deploy(nodes, attrs)
|
|
# See implementation in subclasses, this may be everriden
|
|
attrs['deployment_mode'] ||= 'multinode' # simple multinode deployment is the default
|
|
attrs['use_cinder'] ||= nodes.any?{|n| n['role'] == 'cinder'}
|
|
@ctx.deploy_log_parser.deploy_type = attrs['deployment_mode']
|
|
Astute.logger.info "Deployment mode #{attrs['deployment_mode']}"
|
|
result = self.send("deploy_#{attrs['deployment_mode']}", nodes, attrs)
|
|
end
|
|
|
|
def method_missing(method, *args)
|
|
Astute.logger.error "Method #{method} is not implemented for #{self.class}, raising exception."
|
|
raise "Method #{method} is not implemented for #{self.class}"
|
|
end
|
|
|
|
def attrs_singlenode(nodes, attrs)
|
|
ctrl_management_ip = nodes[0]['network_data'].select {|nd| nd['name'] == 'management'}[0]['ip']
|
|
ctrl_public_ip = nodes[0]['network_data'].select {|nd| nd['name'] == 'public'}[0]['ip']
|
|
attrs['controller_node_address'] = ctrl_management_ip.split('/')[0]
|
|
attrs['controller_node_public'] = ctrl_public_ip.split('/')[0]
|
|
attrs
|
|
end
|
|
|
|
def deploy_singlenode(nodes, attrs)
|
|
# TODO(mihgen) some real stuff is needed
|
|
Astute.logger.info "Starting deployment of single node OpenStack"
|
|
deploy_piece(nodes, attrs)
|
|
end
|
|
|
|
# we mix all attrs and prepare them for Puppet
|
|
# Works for multinode deployment mode
|
|
def attrs_multinode(nodes, attrs)
|
|
ctrl_nodes = nodes.select {|n| n['role'] == 'controller'}
|
|
# TODO(mihgen): we should report error back if there are not enough metadata passed
|
|
ctrl_management_ips = []
|
|
ctrl_public_ips = []
|
|
ctrl_nodes.each do |n|
|
|
ctrl_management_ips << n['network_data'].select {|nd| nd['name'] == 'management'}[0]['ip']
|
|
ctrl_public_ips << n['network_data'].select {|nd| nd['name'] == 'public'}[0]['ip']
|
|
end
|
|
|
|
attrs['controller_node_address'] = ctrl_management_ips[0].split('/')[0]
|
|
attrs['controller_node_public'] = ctrl_public_ips[0].split('/')[0]
|
|
attrs
|
|
end
|
|
|
|
# This method is called by Ruby metaprogramming magic from deploy method
|
|
# It should not contain any magic with attributes, and should not directly run any type of MC plugins
|
|
# It does only support of deployment sequence. See deploy_piece implementation in subclasses.
|
|
def deploy_multinode(nodes, attrs)
|
|
ctrl_nodes = nodes.select {|n| n['role'] == 'controller'}
|
|
Astute.logger.info "Starting deployment of controllers"
|
|
deploy_piece(ctrl_nodes, attrs)
|
|
|
|
compute_nodes = nodes.select {|n| n['role'] == 'compute'}
|
|
Astute.logger.info "Starting deployment of computes"
|
|
deploy_piece(compute_nodes, attrs)
|
|
|
|
other_nodes = nodes - ctrl_nodes - compute_nodes
|
|
Astute.logger.info "Starting deployment of other nodes"
|
|
deploy_piece(other_nodes, attrs)
|
|
return
|
|
end
|
|
|
|
def attrs_ha(nodes, attrs)
|
|
# TODO(mihgen): we should report error back if there are not enough metadata passed
|
|
ctrl_nodes = nodes.select {|n| n['role'] == 'controller'}
|
|
ctrl_manag_addrs = {}
|
|
ctrl_public_addrs = {}
|
|
ctrl_nodes.each do |n|
|
|
# current puppet modules require `hostname -s`
|
|
hostname = n['fqdn'].split(/\./)[0]
|
|
ctrl_manag_addrs.merge!({hostname =>
|
|
n['network_data'].select {|nd| nd['name'] == 'management'}[0]['ip'].split(/\//)[0]})
|
|
ctrl_public_addrs.merge!({hostname =>
|
|
n['network_data'].select {|nd| nd['name'] == 'public'}[0]['ip'].split(/\//)[0]})
|
|
end
|
|
|
|
attrs['ctrl_hostnames'] = ctrl_nodes.map {|n| n['fqdn'].split(/\./)[0]}
|
|
attrs['master_hostname'] = ctrl_nodes[0]['fqdn'].split(/\./)[0]
|
|
attrs['ctrl_public_addresses'] = ctrl_public_addrs
|
|
attrs['ctrl_management_addresses'] = ctrl_manag_addrs
|
|
attrs
|
|
end
|
|
|
|
def deploy_ha(nodes, attrs)
|
|
ctrl_nodes = nodes.select {|n| n['role'] == 'controller'}
|
|
Astute.logger.info "Starting deployment of all controllers one by one, ignoring failure"
|
|
ctrl_nodes.each {|n| deploy_piece([n], attrs, retries=0, change_node_status=false)}
|
|
|
|
Astute.logger.info "Starting deployment of all controllers, ignoring failure"
|
|
deploy_piece(ctrl_nodes, attrs, retries=0, change_node_status=false)
|
|
|
|
Astute.logger.info "Starting deployment of 1st controller again, ignoring failure"
|
|
deploy_piece([ctrl_nodes[0]], attrs, retries=0, change_node_status=false)
|
|
|
|
Astute.logger.info "Starting deployment of all controllers, retries=0"
|
|
deploy_piece(ctrl_nodes, attrs, retries=0, change_node_status=false)
|
|
retries = 3
|
|
Astute.logger.info "Starting deployment of all controllers until it completes, "\
|
|
"allowed retries: #{retries}"
|
|
deploy_piece(ctrl_nodes, attrs, retries=retries)
|
|
|
|
compute_nodes = nodes.select {|n| n['role'] == 'compute'}
|
|
Astute.logger.info "Starting deployment of computes"
|
|
deploy_piece(compute_nodes, attrs)
|
|
|
|
other_nodes = nodes - ctrl_nodes - compute_nodes
|
|
Astute.logger.info "Starting deployment of other nodes"
|
|
deploy_piece(other_nodes, attrs)
|
|
return
|
|
end
|
|
|
|
def deploy_ha_compact(nodes, attrs)
|
|
# Added for backward compatibility with FUEL.
|
|
# Should be used with `simplepuppet' engine only.
|
|
ctrl_nodes = nodes.select {|n| n['role'] == 'controller'}
|
|
compute_nodes = nodes.select {|n| n['role'] == 'compute'}
|
|
other_nodes = nodes - ctrl_nodes - compute_nodes
|
|
|
|
Astute.logger.info "Starting deployment of all controllers one by one, ignoring failure"
|
|
ctrl_nodes.each {|n| deploy_piece([n], attrs, retries=0, change_node_status=false)}
|
|
|
|
Astute.logger.info "Starting deployment of 1st controller again, ignoring failure"
|
|
deploy_piece(ctrl_nodes[0..0], attrs, retries=0, change_node_status=false)
|
|
|
|
Astute.logger.info "Starting deployment of controllers exclude first, ignoring failure"
|
|
deploy_piece(ctrl_nodes[1..-1], attrs, retries=0, change_node_status=false)
|
|
|
|
Astute.logger.info "Starting deployment of 1st controller again"
|
|
deploy_piece(ctrl_nodes[0..0], attrs, retries=0)
|
|
|
|
Astute.logger.info "Starting deployment of controllers exclude first"
|
|
deploy_piece(ctrl_nodes[1..-1], attrs, retries=0)
|
|
|
|
Astute.logger.info "Starting deployment of other nodes"
|
|
deploy_piece(other_nodes, attrs)
|
|
|
|
Astute.logger.info "Starting deployment of computes"
|
|
deploy_piece(compute_nodes, attrs)
|
|
return
|
|
end
|
|
|
|
def deploy_ha_full(nodes, attrs)
|
|
# Added for backward compatibility with FUEL.
|
|
# Should be used with `simplepuppet' engine only.
|
|
ctrl_nodes = nodes.select {|n| n['role'] == 'controller'}
|
|
compute_nodes = nodes.select {|n| n['role'] == 'compute'}
|
|
quantum_nodes = nodes.select {|n| n['role'] == 'quantum'}
|
|
storage_nodes = nodes.select {|n| n['role'] == 'storage'}
|
|
proxy_nodes = nodes.select {|n| n['role'] == 'swift-proxy'}
|
|
other_nodes = nodes - ctrl_nodes - compute_nodes - quantum_nodes - storage_nodes -proxy_nodes
|
|
|
|
Astute.logger.info "Starting deployment of all controllers one by one"
|
|
ctrl_nodes.each {|n| deploy_piece([n], attrs, retries=0)}
|
|
|
|
Astute.logger.info "Starting deployment of 1st controller again"
|
|
deploy_piece(ctrl_nodes[0..0], attrs, retries=0)
|
|
|
|
unless quantum_nodes.empty?
|
|
Astute.logger.info "Starting deployment of Quantum nodes"
|
|
deploy_piece(quantum_nodes, attrs, retries=0)
|
|
end
|
|
|
|
Astute.logger.info "Starting deployment of computes"
|
|
deploy_piece(compute_nodes, attrs)
|
|
|
|
Astute.logger.info "Starting deployment of storages, ignoring failure"
|
|
deploy_piece(storage_nodes, attrs, 2, change_node_status=false)
|
|
|
|
Astute.logger.info "Starting deployment of storages, ignoring failure"
|
|
deploy_piece(storage_nodes, attrs, 2, change_node_status=false)
|
|
|
|
Astute.logger.info "Starting deployment of all proxies one by one, ignoring failure"
|
|
proxy_nodes.each {|n| deploy_piece([n], attrs, retries=0, change_node_status=false)}
|
|
|
|
Astute.logger.info "Starting deployment of storages"
|
|
deploy_piece(storage_nodes, attrs)
|
|
|
|
Astute.logger.info "Starting deployment of proxies"
|
|
deploy_piece(proxy_nodes, attrs)
|
|
|
|
Astute.logger.info "Starting deployment of other nodes"
|
|
deploy_piece(other_nodes, attrs)
|
|
return
|
|
end
|
|
|
|
private
|
|
def nodes_status(nodes, status, data_to_merge)
|
|
{'nodes' => nodes.map { |n| {'uid' => n['uid'], 'status' => status}.merge(data_to_merge) }}
|
|
end
|
|
|
|
def validate_nodes(nodes)
|
|
if nodes.empty?
|
|
Astute.logger.info "#{@ctx.task_id}: Nodes to deploy are not provided. Do nothing."
|
|
return false
|
|
end
|
|
return true
|
|
end
|
|
|
|
def calculate_networks(data)
|
|
interfaces = {}
|
|
data ||= []
|
|
Astute.logger.info "calculate_networks function was provided with #{data.size} interfaces"
|
|
data.each do |iface|
|
|
Astute.logger.debug "Calculating network for #{iface.inspect}"
|
|
if iface['vlan'] and iface['vlan'] != 0
|
|
name = [iface['dev'], iface['vlan']].join('.')
|
|
interfaces[name] = {"vlan" => "yes"}
|
|
else
|
|
name = iface['dev']
|
|
interfaces[name] = {}
|
|
end
|
|
interfaces[name]['bootproto'] = 'none'
|
|
if iface['ip']
|
|
ipaddr = iface['ip'].split('/')[0]
|
|
interfaces[name]['ipaddr'] = ipaddr
|
|
interfaces[name]['netmask'] = iface['netmask'] #=IPAddr.new('255.255.255.255').mask(ipmask[1]).to_s
|
|
interfaces[name]['bootproto'] = 'static'
|
|
if iface['brd']
|
|
interfaces[name]['broadcast'] = iface['brd']
|
|
end
|
|
end
|
|
if iface['gateway'] and iface['name'] =~ /^public$/i
|
|
interfaces[name]['gateway'] = iface['gateway']
|
|
end
|
|
interfaces[name]['ensure'] = 'present'
|
|
Astute.logger.debug "Calculated network for interface: #{name}, data: #{interfaces[name].inspect}"
|
|
end
|
|
interfaces['lo'] = {} unless interfaces.has_key?('lo')
|
|
interfaces['eth0'] = {'bootproto' => 'dhcp',
|
|
'ensure' => 'present'} unless interfaces.has_key?('eth0')
|
|
# Example of return:
|
|
# {"eth0":{"ensure":"present","bootproto":"dhcp"},"lo":{},
|
|
# "eth0.102":{"ipaddr":"10.20.20.20","ensure":"present","vlan":"yes",
|
|
# "netmask":"255.255.255.0","broadcast":"10.20.20.255","bootproto":"static"}}
|
|
return interfaces
|
|
end
|
|
end
|
|
end
|
|
|