1
0
mirror of https://github.com/OpenNebula/one.git synced 2025-01-11 05:17:41 +03:00

F #2645: Add cleanup option and fix some minor bugs (#2954)

This commit is contained in:
Alejandro Huertas Herrero 2019-02-15 16:22:26 +01:00 committed by Tino Vázquez
parent 08538c8156
commit 15533368a8
10 changed files with 281 additions and 184 deletions

View File

@ -85,20 +85,20 @@ class OneProvisionHelper < OpenNebulaHelper::OneHelper
provision.create(config) provision.create(config)
end end
def configure(provision_id, options) def configure(provision_id, force)
provision = OneProvision::Provision.new(provision_id) provision = OneProvision::Provision.new(provision_id)
provision.refresh provision.refresh
provision.configure((options.key? :force)) provision.configure(force)
end end
def delete(provision_id) def delete(provision_id, cleanup)
provision = OneProvision::Provision.new(provision_id) provision = OneProvision::Provision.new(provision_id)
provision.refresh provision.refresh
provision.delete provision.delete(cleanup)
end end
####################################################################### #######################################################################

View File

@ -155,9 +155,9 @@ CommandParser::CmdParser.new(ARGV) do
:format => Integer :format => Integer
} }
DELETE_ALL = { CLEANUP = {
:name => 'delete_all', :name => 'cleanup',
:large => '--delete-all', :large => '--cleanup',
:description => 'Delete all vms and images first, ' \ :description => 'Delete all vms and images first, ' \
'then delete the resources.' 'then delete the resources.'
} }
@ -254,7 +254,7 @@ CommandParser::CmdParser.new(ARGV) do
:options => [MODES, FORCE] do :options => [MODES, FORCE] do
helper.parse_options(options) helper.parse_options(options)
helper.configure(args[0], options) helper.configure(args[0], (options.key?(:force)))
end end
### ###
@ -266,10 +266,10 @@ CommandParser::CmdParser.new(ARGV) do
command :delete, command :delete,
provision_delete_desc, provision_delete_desc,
:provisionid, :provisionid,
:options => [MODES, THREADS, DELETE_ALL] do :options => [MODES, THREADS, CLEANUP] do
helper.parse_options(options) helper.parse_options(options)
helper.delete(args[0]) helper.delete(args[0], (options.key? :cleanup))
end end
######################################################################## ########################################################################

View File

@ -947,16 +947,6 @@ module OpenNebula
end end
end end
private
def action(name)
return Error.new('ID not defined') if !@pe_id
rc = @client.call(VM_METHODS[:action], name, @pe_id)
rc = nil if !OpenNebula.is_error?(rc)
return rc
end
def wait_state(state, timeout=10) def wait_state(state, timeout=10)
vm_state = "" vm_state = ""
lcm_state = "" lcm_state = ""
@ -979,6 +969,16 @@ module OpenNebula
"VM is in state #{vm_state}, #{lcm_state}") "VM is in state #{vm_state}, #{lcm_state}")
end end
private
def action(name)
return Error.new('ID not defined') if !@pe_id
rc = @client.call(VM_METHODS[:action], name, @pe_id)
rc = nil if !OpenNebula.is_error?(rc)
return rc
end
def wait_lcm_state(state, timeout=10) def wait_lcm_state(state, timeout=10)
vm_state = "" vm_state = ""
lcm_state = "" lcm_state = ""

View File

@ -30,11 +30,12 @@ module OneProvision
# Creates a new CLUSTER in OpenNebula # Creates a new CLUSTER in OpenNebula
# #
# @param template [String] Template of the CLUSTER # @param template [String] Template of the CLUSTER
# @param provision_id [String] ID of the provision # @param provision_id [String] ID of the provision
def create(template, provision_id) # @param provision_name [String] Name of the provision
def create(template, provision_id, provision_name)
template['provision']['provision_id'] = provision_id template['provision']['provision_id'] = provision_id
template['provision']['name'] = template['name'] template['provision']['name'] = provision_name
name = template['name'] name = template['name']
template = Utils.template_like_str(template) template = Utils.template_like_str(template)

View File

@ -32,8 +32,10 @@ module OneProvision
# @param template [String] Template of the DATASTORE # @param template [String] Template of the DATASTORE
# @param pm_mad [String] Provision Manager Driver # @param pm_mad [String] Provision Manager Driver
# @param provision_id [String] ID of the provision # @param provision_id [String] ID of the provision
def create(cluster_id, template, pm_mad, provision_id) # @param provision_name [String] Name of the provision
def create(cluster_id, template, pm_mad, provision_id, provision_name)
template['provision']['provision_id'] = provision_id template['provision']['provision_id'] = provision_id
template['provision']['name'] = provision_name
template = Utils.template_like_str(template) template = Utils.template_like_str(template)
template += "PM_MAD=\"#{pm_mad}\"\n" template += "PM_MAD=\"#{pm_mad}\"\n"

View File

@ -180,30 +180,24 @@ module OneProvision
OneProvisionLogger.debug("Offlining OpenNebula host: #{id}") OneProvisionLogger.debug("Offlining OpenNebula host: #{id}")
@@mutex.synchronize do @@mutex.synchronize do
rc = @one.offline Utils.exception(@one.offline)
if OpenNebula.is_error?(rc)
raise OneProvisionLoopException, rc.message
end
end end
end end
# unprovision host if deploy_id
OneProvisionLogger.debug("Undeploying host: #{id}") # unprovision host
OneProvisionLogger.debug("Undeploying host: #{id}")
Driver.pm_driver_action(pm_mad, 'cancel', [deploy_id, name], @one) params = [deploy_id, name]
Driver.pm_driver_action(pm_mad, 'cancel', params, @one)
end
# delete ONE host # delete ONE host
OneProvisionLogger.debug("Deleting OpenNebula host: #{id}") OneProvisionLogger.debug("Deleting OpenNebula host: #{id}")
@@mutex.synchronize do @@mutex.synchronize do
# Fix ubuntu 14.04 borken pipe Utils.exception(@one.delete)
@one.info
rc = @one.delete
if OpenNebula.is_error?(rc)
raise OneProvisionLoopException, rc.message
end
end end
end end

View File

@ -60,35 +60,32 @@ module OneProvision
@name = @clusters[0]['TEMPLATE/PROVISION/NAME'] @name = @clusters[0]['TEMPLATE/PROVISION/NAME']
end end
# TODO: rename delete_all -> cleanup
#
# Deletes the PROVISION # Deletes the PROVISION
# #
def delete # @param cleanup [Boolean] True to delete running VMs and images
def delete(cleanup = false)
Utils.fail('Provision not found.') unless exists Utils.fail('Provision not found.') unless exists
if running_vms? && !cleanup
Utils.fail('Provision with running VMs can\'t be deleted')
end
if images? && !cleanup
Utils.fail('Provision with images can\'t be deleted')
end
delete_vms if cleanup
delete_images if cleanup
OneProvisionLogger.info("Deleting provision #{@id}") OneProvisionLogger.info("Deleting provision #{@id}")
# offline and (optionally) clean all hosts # offline and (optionally) clean all hosts
OneProvisionLogger.debug('Offlining OpenNebula hosts') OneProvisionLogger.debug('Offlining OpenNebula hosts')
@hosts.each do |h| @hosts.each do |host|
host = Host.new(h['ID'])
Driver.retry_loop 'Failed to offline host' do Driver.retry_loop 'Failed to offline host' do
rc = h.offline Utils.exception(host.offline)
if OpenNebula.is_error?(rc)
raise OneProvisionLoopException, rc.message
end
rc = h.info
if OpenNebula.is_error?(rc)
raise OneProvisionLoopException, rc.message
end
end
if host.running_vms?
Utils.fail('Provision with running VMs can\'t be deleted')
end end
end end
@ -97,25 +94,27 @@ module OneProvision
threads = [] threads = []
@hosts.each do |host| Driver.retry_loop 'Failed to delete hosts' do
host = Host.new(host['ID']) @hosts.each do |host|
host = Host.new(host['ID'])
if Options.threads > 1 if Options.threads > 1
while Thread.list.count > Options.threads while Thread.list.count > Options.threads
threads.map do |thread| threads.map do |thread|
thread.join(5) thread.join(5)
end
end end
end
threads << Thread.new do threads << Thread.new do
host.delete
end
else
host.delete host.delete
end end
else
host.delete
end end
end
threads.map(&:join) threads.map(&:join)
end
# delete all other deployed objects # delete all other deployed objects
OneProvisionLogger.info('Deleting provision objects') OneProvisionLogger.info('Deleting provision objects')
@ -127,13 +126,7 @@ module OneProvision
Driver.retry_loop "Failed to delete #{msg}" do Driver.retry_loop "Failed to delete #{msg}" do
OneProvisionLogger.debug("Deleting OpenNebula #{msg}") OneProvisionLogger.debug("Deleting OpenNebula #{msg}")
# Fix ubuntu 14.04 broken pipe Utils.exception(obj.delete)
obj.info
rc = obj.delete
if OpenNebula.is_error?(rc)
raise OneProvisionLoopException, rc.message
end
end end
end end
end end
@ -170,7 +163,7 @@ module OneProvision
def create(config) def create(config)
Ansible.check_ansible_version Ansible.check_ansible_version
Driver.retry_loop 'Failed to create provision' do begin
# read provision file # read provision file
cfg = Utils.create_config(Utils.read_config(config)) cfg = Utils.create_config(Utils.read_config(config))
@ -178,12 +171,19 @@ module OneProvision
OneProvisionLogger.info('Creating provision objects') OneProvisionLogger.info('Creating provision objects')
cluster = create_cluster(cfg) cluster = nil
cid = cluster.id cid = nil
Driver.retry_loop 'Failed to create cluster' do
cluster = create_cluster(cfg)
cid = cluster.id
end
Mode.new_cleanup(true) Mode.new_cleanup(true)
create_resources(cfg, cid) Driver.retry_loop 'Failed to create some resources' do
create_resources(cfg, cid)
end
if cfg['hosts'].nil? if cfg['hosts'].nil?
puts "ID: #{@id}" puts "ID: #{@id}"
@ -191,35 +191,39 @@ module OneProvision
return 0 return 0
end end
begin Driver.retry_loop 'Failed to create hosts' do
create_hosts(cfg, cid) create_hosts(cfg, cid)
# ask user to be patient, mandatory for now
STDERR.puts 'WARNING: This operation can ' \
'take tens of minutes. Please be patient.'
OneProvisionLogger.info('Deploying')
deploy_ids = deploy_hosts
if deploy_ids.nil? || deploy_ids.empty?
Utils.fail('Deployment failed, no ID got from driver')
end
OneProvisionLogger.info('Monitoring hosts')
update_hosts(deploy_ids)
Ansible.configure(@hosts)
puts "ID: #{@id}"
0
rescue OneProvisionCleanupException
delete
-1
end end
# ask user to be patient, mandatory for now
STDERR.puts 'WARNING: This operation can ' \
'take tens of minutes. Please be patient.'
OneProvisionLogger.info('Deploying')
deploy_ids = nil
Driver.retry_loop 'Failed to deploy hosts' do
deploy_ids = deploy_hosts
end
if deploy_ids.nil? || deploy_ids.empty?
Utils.fail('Deployment failed, no ID got from driver')
end
OneProvisionLogger.info('Monitoring hosts')
update_hosts(deploy_ids)
Ansible.configure(@hosts)
puts "ID: #{@id}"
0
rescue OneProvisionCleanupException
delete
-1
end end
end end
@ -239,23 +243,19 @@ module OneProvision
# #
# @return [OpenNebula::Cluster] The new cluster # @return [OpenNebula::Cluster] The new cluster
def create_cluster(cfg) def create_cluster(cfg)
cluster = nil msg = "Creating OpenNebula cluster: #{cfg['cluster']['name']}"
Driver.retry_loop 'Failed to create cluster' do OneProvisionLogger.debug(msg)
msg = "Creating OpenNebula cluster: #{cfg['cluster']['name']}"
OneProvisionLogger.debug(msg) # create new cluster
cluster = Cluster.new
cluster.create(cfg['cluster'], @id, @name)
cluster = cluster.one
cid = cluster.id
# create new cluster @clusters << cluster
cluster = Cluster.new
cluster.create(cfg['cluster'], @id)
cluster = cluster.one
cid = cluster.id
@clusters << cluster OneProvisionLogger.debug("cluster created with ID: #{cid}")
OneProvisionLogger.debug("cluster created with ID: #{cid}")
end
cluster cluster
end end
@ -269,43 +269,32 @@ module OneProvision
next if cfg[r].nil? next if cfg[r].nil?
cfg[r].each do |x| cfg[r].each do |x|
begin if cfg['defaults'] && cfg['defaults']['driver']
if cfg['defaults'] && cfg['defaults']['driver'] driver = cfg['defaults']['provision']['driver']
driver = cfg['defaults']['provision']['driver']
end
r_name = "#{r}: #{x['name']}"
Driver.retry_loop "Failed to create #{r_name}" do
msg = "Creating OpenNebula #{r_name}"
OneProvisionLogger.debug(msg)
erb = Utils.evaluate_erb(self, x)
if r == 'datastores'
datastore = Datastore.new
datastore.create(cid.to_i, erb, driver, @id)
@datastores << datastore.one
else
vnet = Vnet.new
vnet.create(cid.to_i, erb, driver, @id)
@vnets << vnet.one
end
r = 'vnets' if r == 'networks'
rid = instance_variable_get("@#{r}").last['ID']
rname = r.chomp('s').capitalize
msg = "#{rname} created with ID: #{rid}"
OneProvisionLogger.debug(msg)
end
rescue OneProvisionCleanupException
refresh
delete
-1
end end
msg = "Creating OpenNebula #{r}: #{x['name']}"
OneProvisionLogger.debug(msg)
erb = Utils.evaluate_erb(self, x)
if r == 'datastores'
datastore = Datastore.new
datastore.create(cid.to_i, erb, driver, @id, @name)
@datastores << datastore.one
else
vnet = Vnet.new
vnet.create(cid.to_i, erb, driver, @id, @name)
@vnets << vnet.one
end
r = 'vnets' if r == 'networks'
rid = instance_variable_get("@#{r}").last['ID']
rname = r.chomp('s').capitalize
msg = "#{rname} created with ID: #{rid}"
OneProvisionLogger.debug(msg)
end end
end end
end end
@ -317,7 +306,7 @@ module OneProvision
def create_hosts(cfg, cid) def create_hosts(cfg, cid)
cfg['hosts'].each do |h| cfg['hosts'].each do |h|
erb = Utils.evaluate_erb(self, h) erb = Utils.evaluate_erb(self, h)
dfile = Utils .create_deployment_file(erb, @id) dfile = Utils .create_deployment_file(erb, @id, @name)
playbook = cfg['playbook'] playbook = cfg['playbook']
host = Host.new host = Host.new
@ -394,6 +383,111 @@ module OneProvision
end end
end end
# Checks if the PROVISION has running VMs
#
# @return [Boolean] True if there are running VMs
def running_vms?
@hosts.each do |host|
Utils.exception(host.info)
return true if host['HOST_SHARE/RUNNING_VMS'].to_i > 0
end
false
end
# Checks if the PROVISION has images in its datastores
#
# @return [Boolean] True if there are images
def images?
@datastores.each do |datastore|
Utils.exception(datastore.info)
images = datastore.retrieve_elements('IMAGES/ID')
return true if images
end
false
end
# Deletes VMs from the PROVISION
def delete_vms
Driver.retry_loop 'Failed to delete running_vms' do
hosts = []
@hosts.each do |host|
Utils.exception(host.info)
hosts << host if host['HOST_SHARE/RUNNING_VMS'].to_i > 0
end
hosts.each do |host|
vm_ids = host.retrieve_elements('VMS/ID')
vm_ids.each do |id|
delete_object('vm', id)
end
end
if running_vms?
raise OneProvisionLoopException, 'Still found running VMs'
end
end
end
# Deletes images from the PROVISION
def delete_images
Driver.retry_loop 'Failed to delete images' do
datastores = []
@datastores.each do |datastore|
Utils.exception(datastore.info)
images = datastore.retrieve_elements('IMAGES/ID')
datastores << datastore if images
end
datastores.each do |datastore|
image_ids = datastore.retrieve_elements('IMAGES/ID')
image_ids.each do |id|
delete_object('image', id)
end
end
if images?
raise OneProvisionLoopException, 'Still found images'
end
end
end
# Deletes an object
#
# @param type [String] Type of the object (vm, image)
# @param id [String] ID of the object
def delete_object(type, id)
msg = "Deleting OpenNebula #{type} #{id}"
OneProvision::OneProvisionLogger.debug(msg)
object = nil
client = OpenNebula::Client.new
if type == 'vm'
object = OpenNebula::VirtualMachine.new_with_id(id, client)
else
object = OpenNebula::Image.new_with_id(id, client)
end
Utils.exception(object.info)
Utils.exception(object.delete)
Utils.exception(object.wait_state('DONE')) if type == 'vm'
end
end end
end end

View File

@ -79,9 +79,7 @@ module OneProvision
rc = @one.allocate(template, cluster) rc = @one.allocate(template, cluster)
end end
if OpenNebula.is_error?(rc) Utils.exception(rc)
Utils.fail(rc.message)
end
if @type == 'Cluster' if @type == 'Cluster'
@one.update(template, true) @one.update(template, true)
@ -97,11 +95,7 @@ module OneProvision
# @return [Array] with provision resource if id!=nil # @return [Array] with provision resource if id!=nil
# with all resources if if==nil # with all resources if if==nil
def get(id = nil) def get(id = nil)
rc = @pool.info Utils.exception(@pool.info)
if OpenNebula.is_error?(rc)
Utils.fail(rc.message)
end
if id if id
return @pool.select do |resource| return @pool.select do |resource|

View File

@ -164,7 +164,11 @@ module OneProvision
sections = %w[connection provision configuration] sections = %w[connection provision configuration]
sections.each do |section| sections.each do |section|
data = CONFIG_DEFAULTS[section] || {} data = CONFIG_DEFAULTS[section] || {}
defaults = yaml['defaults'][section]
if yaml['defaults']
defaults = yaml['defaults'][section]
end
h_sec = host[section] h_sec = host[section]
# merge defaults with globals # merge defaults with globals
# and device specific params # and device specific params
@ -182,24 +186,21 @@ module OneProvision
next unless yaml[r] next unless yaml[r]
yaml[r] = yaml[r].map do |x| yaml[r] = yaml[r].map do |x|
if defaults && x['provision'] x['provision'] ||= {}
if defaults && defaults.key?('provision')
x['provision'].merge!(defaults['provision']) x['provision'].merge!(defaults['provision'])
else
x['provision'] = {} unless x['provision']
end end
x x
end end
end end
cluster_p = yaml['cluster']['provision'] yaml['cluster']['provision'] ||= {}
if defaults && cluster_p if defaults && defaults.key?('provision')
yaml['cluster']['provision'] yaml['cluster']['provision']
.merge!(defaults['provision']) .merge!(defaults['provision'])
else
yaml['cluster']['provision'] = {} unless cluster_p
end end
rescue StandardError => e rescue StandardError => e
Utils.fail("Failed to read configuration: #{e}") Utils.fail("Failed to read configuration: #{e}")
@ -305,11 +306,12 @@ module OneProvision
# Creates the host deployment file # Creates the host deployment file
# #
# @param host [Hash] Hash with host information # @param host [Hash] Hash with host information
# @param provision_id [String] ID of the provision # @param provision_id [String] ID of the provision
# @param provision_name [String] Name of the provision
# #
# @return [Nokogiri::XML] XML with the host information # @return [Nokogiri::XML] XML with the host information
def create_deployment_file(host, provision_id) def create_deployment_file(host, provision_id, provision_name)
ssh_key = try_read_file(host['connection']['public_key']) ssh_key = try_read_file(host['connection']['public_key'])
config = Base64.strict_encode64(host['configuration'].to_yaml) config = Base64.strict_encode64(host['configuration'].to_yaml)
@ -328,6 +330,7 @@ module OneProvision
end end
end end
xml.send('PROVISION_ID', provision_id) xml.send('PROVISION_ID', provision_id)
xml.send('NAME', provision_name)
end end
if host['configuration'] if host['configuration']
xml.PROVISION_CONFIGURATION_BASE64 config xml.PROVISION_CONFIGURATION_BASE64 config
@ -361,6 +364,13 @@ module OneProvision
exit(code) exit(code)
end end
# Checks if the return_code is error
def exception(return_code)
error = OpenNebula.is_error?(return_code)
raise OneProvisionLoopException, return_code.message if error
end
# Gets error message # Gets error message
# #
# @param text [String] Text with error message inside # @param text [String] Text with error message inside

View File

@ -28,12 +28,14 @@ module OneProvision
# Creates a new VNET in OpenNebula # Creates a new VNET in OpenNebula
# #
# @param cluster_id [Integer] ID of the CLUSTER where is the VNET # @param cluster_id [Integer] ID of the CLUSTER where is the VNET
# @param template [String] Template of the VNET # @param template [String] Template of the VNET
# @param pm_mad [String] Provision Manager Driver # @param pm_mad [String] Provision Manager Driver
# @param provision_id [String] ID of the provision # @param provision_id [String] ID of the provision
def create(cluster_id, template, pm_mad, provision_id) # @param provision_name [String] Name of the provision
def create(cluster_id, template, pm_mad, provision_id, provision_name)
template['provision']['provision_id'] = provision_id template['provision']['provision_id'] = provision_id
template['provision']['name'] = provision_name
template = Utils.template_like_str(template) template = Utils.template_like_str(template)
template += "PM_MAD=\"#{pm_mad}\"\n" template += "PM_MAD=\"#{pm_mad}\"\n"