From 565e0f3187ebc9efa2c63e36cb5e71165c69ad3f Mon Sep 17 00:00:00 2001 From: Daniel Clavijo Coca Date: Mon, 18 Feb 2019 11:17:37 -0600 Subject: [PATCH] Feature 1685: Add better checks, minor code refactor --- src/vmm_mad/remotes/lib/lxd/client.rb | 2 + src/vmm_mad/remotes/lib/lxd/container.rb | 10 +- src/vmm_mad/remotes/lib/lxd/mapper/mapper.rb | 139 ++++++++++--------- src/vmm_mad/remotes/lib/lxd/opennebula_vm.rb | 12 +- src/vmm_mad/remotes/lxd/deploy | 19 ++- src/vmm_mad/remotes/lxd/shutdown | 6 +- 6 files changed, 105 insertions(+), 83 deletions(-) diff --git a/src/vmm_mad/remotes/lib/lxd/client.rb b/src/vmm_mad/remotes/lib/lxd/client.rb index 0f500d15d0..9fda605fbf 100644 --- a/src/vmm_mad/remotes/lib/lxd/client.rb +++ b/src/vmm_mad/remotes/lib/lxd/client.rb @@ -96,6 +96,8 @@ class LXDClient response = get("operations/#{operation_id}/wait#{timeout}") raise LXDError, response if response['metadata']['status'] == 'Failure' + + response end private diff --git a/src/vmm_mad/remotes/lib/lxd/container.rb b/src/vmm_mad/remotes/lib/lxd/container.rb index 5738a7f1dd..537b9d79b6 100644 --- a/src/vmm_mad/remotes/lib/lxd/container.rb +++ b/src/vmm_mad/remotes/lib/lxd/container.rb @@ -139,6 +139,7 @@ class Container cmd = "#{Mapper::COMMANDS[:lsblk]} -J" _rc, o, _e = Command.execute(cmd, false) + # TODO: Add extra mounts to raise raise "Container rootfs still mounted \n#{o}" if o.include?(@rootfs_dir) wait?(@client.delete("#{CONTAINERS}/#{name}"), wait, timeout) @@ -182,8 +183,13 @@ class Container change_state(__method__, options) end - def stop(options = {}) + def stop(options = { :timeout => 120 }) change_state(__method__, options) + rescue StandardError => exception + raise exception unless exception.class == Net::ReadTimeout + + OpenNebula.log_error "Timeout detected\n#{exception}\nForcing shutdown" + stop(:force => true) end def restart(options = {}) @@ -402,7 +408,7 @@ class Container options.update(:action => action) response = @client.put("#{CONTAINERS}/#{name}/state", options) - wait?(response, options[:wait], options[:timeout]) + status = wait?(response, options[:wait], options[:timeout]) @lxc = @client.get("#{CONTAINERS}/#{name}")['metadata'] diff --git a/src/vmm_mad/remotes/lib/lxd/mapper/mapper.rb b/src/vmm_mad/remotes/lib/lxd/mapper/mapper.rb index bbc5446e13..4e64169591 100644 --- a/src/vmm_mad/remotes/lib/lxd/mapper/mapper.rb +++ b/src/vmm_mad/remotes/lib/lxd/mapper/mapper.rb @@ -92,7 +92,7 @@ class Mapper # @param disk [XMLElement] with the disk data # @param directory [String] where the disk has to be mounted # - # @return nil + # @return nil def do_unmap(device, one_vm, disk, directory) OpenNebula.log_error("unmap function not implemented for #{self.class}") return nil @@ -256,76 +256,21 @@ class Mapper # @param partitions [Array] with partition device names # @param path [String] to directory to mount the disk partitions def mount(partitions, path) + # TODO: Unmap device if mount fails # Single partition # ---------------- return mount_dev(partitions[0]['path'], path) if partitions.size == 1 # Multiple partitions # ------------------- - rc = true - fstab = '' + fstab = find_fstab(partitions, path) - # Look for fstab and mount rootfs in path. First partition with - # a /etc/fstab file is used as rootfs and it is kept mounted - partitions.each do |p| - OpenNebula.log("Looking for fstab on #{p['path']}") - - rc = mount_dev(p['path'], path) - - return false if !rc - - bin = COMMANDS[:catfstab] - bin = COMMANDS[:cat] unless path.include?('containers/one-') - - cmd = "#{bin} #{path}/etc/fstab" - - _rc, fstab, _e = Command.execute(cmd, false) - - if fstab.empty? - return false unless umount_dev(p['path']) - - next - end - - OpenNebula.log("Found fstab on #{p['path']}") - break - end - - if fstab.empty? - OpenNebula.log_error('No fstab file found') + if !fstab + # TODO: Unmap the device return false end - # Parse fstab contents & mount partitions - fstab.each_line do |l| - next if l.strip.chomp.empty? - next if l =~ /\s*#/ - - fs, mount_point, type, opts, dump, pass = l.split - - if l =~ /^\s*LABEL=/ # disk by LABEL - value = fs.split("=").last.strip.chomp - key = 'label' - elsif l =~ /^\s*UUID=/ #disk by UUID - value = fs.split("=").last.strip.chomp - key = 'uuid' - else #disk by device - NOT SUPPORTED or other FS - next - end - - next if %w[/ swap].include?(mount_point) - - partitions.each { |p| - next if p[key] != value - - rc = mount_dev(p['path'], path + mount_point) - return false if !rc - - break - } - end - - rc + parse_fstab(partitions, path, fstab) end # -------------------------------------------------------------------------- @@ -349,8 +294,6 @@ class Mapper rc, _out, err = Command.execute("#{COMMANDS[:mount]} #{dev} #{path}", true) if rc != 0 - return true if err.include?("unknown filesystem type 'swap'") - OpenNebula.log_error("mount_dev: #{err}") return false end @@ -436,10 +379,12 @@ class Mapper false end + # Extracts the partiton table from a device def show_parts(device) action_parts(device, '-s -av') end + # Hides the partiton table from a device def hide_parts(device) action_parts(device, '-d') end @@ -459,7 +404,7 @@ class Mapper _rc, out, _err = Command.execute("#{COMMANDS[:lsblk]} -J", false) if out.include?(path) - OpenNebula.log_error("mount_dev: Mount detected in #{path}") + OpenNebula.log_error("#{__method__}: Mount detected in #{path}") return true end false @@ -480,4 +425,70 @@ class Mapper false end + # Look for fstab and mount rootfs in path. First partition with + # a /etc/fstab file is used as rootfs and it is kept mounted + def find_fstab(partitions, path) + fstab = '' + partitions.each do |p| + OpenNebula.log("Looking for fstab on #{p['path']}") + + rc = mount_dev(p['path'], path) + next unless rc + + bin = COMMANDS[:catfstab] + bin = COMMANDS[:cat] unless path.include?('containers/one-') + + cmd = "#{bin} #{path}/etc/fstab" + + _rc, fstab, _e = Command.execute(cmd, false) + + if fstab.empty? + return false unless umount_dev(p['path']) + + next + end + + OpenNebula.log("Found fstab on #{p['path']}") + break + end + + return fstab unless fstab.empty? + + OpenNebula.log_error('No fstab file found') + + false + end + + # Parse fstab contents & mount partitions + def parse_fstab(partitions, path, fstab) + fstab.each_line do |l| + next if l.strip.chomp.empty? + next if l =~ /\s*#/ + + fs, mount_point, _type, _opts, _dump, _pass = l.split + + if l =~ /^\s*LABEL=/ # disk by LABEL + value = fs.split('=').last.strip.chomp + key = 'label' + elsif l =~ /^\s*UUID=/ # disk by UUID + value = fs.split('=').last.strip.chomp + key = 'uuid' + else # disk by device - NOT SUPPORTED or other FS + next + end + + next if %w[/ swap].include?(mount_point) + + partitions.each {|p| + next if p[key] != value + + return false unless mount_dev(p['path'], path + mount_point) + + break + } + end + + true + end + end diff --git a/src/vmm_mad/remotes/lib/lxd/opennebula_vm.rb b/src/vmm_mad/remotes/lib/lxd/opennebula_vm.rb index fbd95b7fa9..1289598135 100644 --- a/src/vmm_mad/remotes/lib/lxd/opennebula_vm.rb +++ b/src/vmm_mad/remotes/lib/lxd/opennebula_vm.rb @@ -19,9 +19,10 @@ require 'yaml' # This class reads and holds configuration attributes for the LXD driver class LXDConfiguration < Hash + # TODO: Create lxdrc file from this hash to avoid duplicated config DEFAULT_CONFIGURATION = { :vnc => { - :command => '/bin/bash', + :command => '/bin/login', :width => '800', :height => '600', :timeout => '300' @@ -183,7 +184,7 @@ class OpenNebulaVM end end - def get_context_disk() + def get_context_disk @xml.element('//TEMPLATE/CONTEXT') end @@ -376,15 +377,16 @@ class OpenNebulaVM # Creates or closes a connection to a container rfb port depending on signal def vnc_command(signal) data = @xml.element('//TEMPLATE/GRAPHICS') - return unless data && data['PORT'] && data['TYPE'] && data['TYPE'].casecmp('vnc').zero? + return unless data && data['TYPE'].casecmp('vnc').zero? pass = data['PASSWD'] - pass = '-' unless pass && !pass.empty? + pass = '-' if pass.empty? case signal when 'start' - # TODO: Allow to set vnc command on VM template command = @lxdrc[:vnc][:command] + command = data['COMMAND'] unless data['COMMAND'].empty? + "#{data['PORT']} #{pass} lxc exec #{@vm_name} #{command}\n" when 'stop' "-#{data['PORT']}\n" diff --git a/src/vmm_mad/remotes/lxd/deploy b/src/vmm_mad/remotes/lxd/deploy index 700a3a525b..04ef7ad911 100755 --- a/src/vmm_mad/remotes/lxd/deploy +++ b/src/vmm_mad/remotes/lxd/deploy @@ -71,16 +71,21 @@ else mapped = container.setup_storage('map') raise 'failed to setup container storage' unless mapped - if container.start != 'Running' - OpenNebula.log_error('Container failed to start') + begin + operation = container.start + raise operation if container.status != 'Running' + rescue LXDError => exception + storage_deleted = container.setup_storage('unmap') - deleted = container.setup_storage('unmap') - raise 'failed to dismantle container storage' unless deleted + if storage_deleted + container.delete + else + OpenNebula.log_error 'failed to dismantle container storage' + end - container.delete - - raise LXDError, container.status + raise LXDError, exception end + end #------------------------------------------------------------------------------- diff --git a/src/vmm_mad/remotes/lxd/shutdown b/src/vmm_mad/remotes/lxd/shutdown index d56c1e681e..f42321ce0d 100755 --- a/src/vmm_mad/remotes/lxd/shutdown +++ b/src/vmm_mad/remotes/lxd/shutdown @@ -44,13 +44,9 @@ end if !container.wild? unmapped = container.setup_storage('unmap') - unless unmapped - container.start - raise 'Failed to dismantle container storage' - end + raise 'Failed to dismantle container storage' unless unmapped container.delete - end container.vnc('stop')