diff --git a/src/vmm_mad/remotes/lib/lxd/client.rb b/src/vmm_mad/remotes/lib/lxd/client.rb index e3cb989717..db13bfceb3 100644 --- a/src/vmm_mad/remotes/lib/lxd/client.rb +++ b/src/vmm_mad/remotes/lib/lxd/client.rb @@ -31,14 +31,25 @@ class LXDClient API = '/1.0'.freeze HEADER = { 'Host' => 'localhost' }.freeze - SOCK_PATH = '/var/lib/lxd/unix.socket' + attr_reader :lxd_path - # Enable communication with LXD via unix socket - begin - SOCK = Net::BufferedIO.new(UNIXSocket.new(SOCK_PATH)) - rescue StandardError - STDERR.puts('Could not open LXD socket') - Process.exit(1) + def initialize + paths = ['/var/lib/lxd', '/var/snap/lxd/common/lxd'] + + @socket = nil + @lxd_path = nil + + paths.each do |path| + begin + @socket = socket(path) + @lxd_path = path + break + rescue + next + end + end + + raise 'Failed to open LXD socket' unless @socket end # Performs HTTP::Get @@ -89,6 +100,11 @@ class LXDClient private + # Enable communication with LXD via unix socket + def socket(lxd_path) + Net::BufferedIO.new(UNIXSocket.new("#{lxd_path}/unix.socket")) + end + # Returns the HTTPResponse body as a hash # Params: # +request+:: +Net::HTTP::Request+ made to the http server @@ -98,16 +114,16 @@ class LXDClient def get_response(request, data) request.body = JSON.dump(data) unless data.nil? - request.exec(SOCK, '1.1', request.path) + request.exec(@socket, '1.1', request.path) response = nil loop do - response = Net::HTTPResponse.read_new(SOCK) + response = Net::HTTPResponse.read_new(@socket) break unless response.is_a?(Net::HTTPContinue) end - response.reading_body(SOCK, request.response_body_permitted?) {} + response.reading_body(@socket, request.response_body_permitted?) {} response = JSON.parse(response.body) diff --git a/src/vmm_mad/remotes/lib/lxd/container.rb b/src/vmm_mad/remotes/lib/lxd/container.rb index ceb24f9566..a0f8189c99 100644 --- a/src/vmm_mad/remotes/lib/lxd/container.rb +++ b/src/vmm_mad/remotes/lib/lxd/container.rb @@ -68,53 +68,58 @@ class Container @lxc = lxc @one = one + + @containers = "#{@client.lxd_path}/storage-pools/default/containers" + @rootfs_dir = "#{@containers}/#{name}/rootfs" + + @context_path = "#{@rootfs_dir}/context" end class << self - # Returns specific container, by its name - # Params: - # +name+:: container name - def get(name, one_xml, client) - info = client.get("#{CONTAINERS}/#{name}")['metadata'] + # Returns specific container, by its name + # Params: + # +name+:: container name + def get(name, one_xml, client) + info = client.get("#{CONTAINERS}/#{name}")['metadata'] - one = nil - one = OpenNebulaVM.new(one_xml) if one_xml + one = nil + one = OpenNebulaVM.new(one_xml) if one_xml - Container.new(info, one, client) - rescue LXDError => exception - raise exception - end - - # Creates container from a OpenNebula VM xml description - def new_from_xml(one_xml, client) - one = OpenNebulaVM.new(one_xml) - - Container.new(one.to_lxc, one, client) - end - - # Returns an array of container objects - def get_all(client) - containers = [] - - container_names = client.get(CONTAINERS)['metadata'] - container_names.each do |name| - name = name.split('/').last - containers.push(get(name, nil, client)) + Container.new(info, one, client) + rescue LXDError => exception + raise exception end - containers - end + # Creates container from a OpenNebula VM xml description + def new_from_xml(one_xml, client) + one = OpenNebulaVM.new(one_xml) - # Returns boolean indicating if the container exists(true) or not (false) - def exist?(name, client) - client.get("#{CONTAINERS}/#{name}") - true - rescue LXDError => exception - raise exception if exception.body['error_code'] != 404 + Container.new(one.to_lxc, one, client) + end - false - end + # Returns an array of container objects + def get_all(client) + containers = [] + + container_names = client.get(CONTAINERS)['metadata'] + container_names.each do |name| + name = name.split('/').last + containers.push(get(name, nil, client)) + end + + containers + end + + # Returns boolean indicating if the container exists(true) or not (false) + def exist?(name, client) + client.get("#{CONTAINERS}/#{name}") + true + rescue LXDError => exception + raise exception if exception.body['error_code'] != 404 + + false + end end @@ -131,6 +136,10 @@ class Container # Delete container def delete(wait: true, timeout: '') + unless Dir.empty?(@rootfs_dir) || (Dir["#{@rootfs_dir}/*"] == [@context_path] && Dir.empty?(@context_path)) + raise 'Container rootfs not empty' + end + wait?(@client.delete("#{CONTAINERS}/#{name}"), wait, timeout) end @@ -222,8 +231,7 @@ class Container context = @one.get_context_disk mapper = FSRawMapper.new - context_path = "#{@one.lxdrc[:containers]}/#{name}/rootfs/context" - create_context_dir = "#{Mapper::COMMANDS[:su_mkdir]} #{context_path}" + create_context_dir = "#{Mapper::COMMANDS[:su_mkdir]} #{@context_path}" rc, _o, e = Command.execute(create_context_dir, false) @@ -330,7 +338,7 @@ class Container disk_id = disk['DISK_ID'] if disk_id == @one.rootfs_id - target = "#{@one.lxdrc[:containers]}/#{name}/rootfs" + target = @rootfs_dir else target = @one.disk_mountpoint(disk_id) end diff --git a/src/vmm_mad/remotes/lib/lxd/mapper/mapper.rb b/src/vmm_mad/remotes/lib/lxd/mapper/mapper.rb index 1ce0f1e06d..cd141895d7 100644 --- a/src/vmm_mad/remotes/lib/lxd/mapper/mapper.rb +++ b/src/vmm_mad/remotes/lib/lxd/mapper/mapper.rb @@ -190,19 +190,19 @@ class Mapper device = '' real_path = directory - ds = one_vm.lxdrc[:datastore_location] + "/#{one_vm.sysds_id}" + ds = one_vm.sysds_path if File.symlink?(ds) real_ds = File.readlink(ds) real_path = real_ds + directory.split(ds)[-1] if directory.include?(ds) end - + sys_parts.each { |d| if d['mountpoint'] == real_path partitions = [d] device = d['path'] break end - + d['children'].each { |c| if c['mountpoint'] == real_path partitions = d['children'] @@ -213,13 +213,18 @@ class Mapper break if !partitions.empty? } - + partitions.delete_if { |p| !p['mountpoint'] } partitions.sort! { |a,b| b['mountpoint'].length <=> a['mountpoint'].length } + if device.empty? + OpenNebula.log_error("Failed to detect block device from #{directory}") + return + end + return unless umount(partitions) return unless do_unmap(device, one_vm, disk, real_path) @@ -330,6 +335,18 @@ class Mapper def mount_dev(dev, path) OpenNebula.log_info "Mounting #{dev} at #{path}" + rc, out, err = Command.execute("#{COMMANDS[:lsblk]} -J", false) + + if rc != 0 || out.empty? + OpenNebula.log_error("mount_dev: #{err}") + return false + end + + if out.match?(path) + OpenNebula.log_error("mount_dev: Mount detected in #{path}") + return false + end + if path =~ /.*\/rootfs/ cmd = COMMANDS[:su_mkdir] else diff --git a/src/vmm_mad/remotes/lib/lxd/mapper/qcow2.rb b/src/vmm_mad/remotes/lib/lxd/mapper/qcow2.rb index a97ae56293..d5aea751df 100644 --- a/src/vmm_mad/remotes/lib/lxd/mapper/qcow2.rb +++ b/src/vmm_mad/remotes/lib/lxd/mapper/qcow2.rb @@ -33,8 +33,7 @@ class Qcow2Mapper < Mapper dsrc = disk_source(one_vm, disk) cmd = "#{COMMANDS[:nbd]} -c #{device} #{dsrc}" - ds = one_vm.lxdrc[:datastore_location] + "/#{one_vm.sysds_id}" - File.chmod(0664, dsrc) if File.symlink?(ds) + File.chmod(0664, dsrc) if File.symlink?(one_vm.sysds_path) rc, _out, err = Command.execute(cmd, true) diff --git a/src/vmm_mad/remotes/lib/lxd/opennebula_vm.rb b/src/vmm_mad/remotes/lib/lxd/opennebula_vm.rb index d62193d6c8..4734e4bbd4 100644 --- a/src/vmm_mad/remotes/lib/lxd/opennebula_vm.rb +++ b/src/vmm_mad/remotes/lib/lxd/opennebula_vm.rb @@ -25,9 +25,7 @@ class LXDConfiguration < Hash :width => '800', :height => '600', :timeout => '300' - }, - :datastore_location => '/var/lib/one/datastores', - :containers => '/var/lib/lxd/storage-pools/default/containers' + } } def initialize @@ -44,7 +42,7 @@ end # This class parses and wraps the information in the Driver action data class OpenNebulaVM - attr_reader :xml, :vm_id, :vm_name, :sysds_id, :ds_path, :rootfs_id, :lxdrc + attr_reader :xml, :vm_id, :vm_name, :sysds_id, :ds_path, :rootfs_id, :lxdrc, :sysds_path #--------------------------------------------------------------------------- # Class Constructor @@ -64,13 +62,14 @@ class OpenNebulaVM # Load Driver configuration @lxdrc = LXDConfiguration.new - @ds_path = @lxdrc[:datastore_location] - # Sets the DISK ID of the root filesystem disk = @xml.element('//TEMPLATE/DISK') return unless disk + @ds_path = get_dspath(disk) + @sysds_path = "#{@ds_path}/#{@sysds_id}" + @rootfs_id = disk['DISK_ID'] boot_order = @xml['//TEMPLATE/OS/BOOT'] @rootfs_id = boot_order.split(',')[0][-1] unless boot_order.empty? @@ -373,6 +372,25 @@ class OpenNebulaVM mapped end + # Returns the datastores BASE_PATH location + def get_dspath(disk) + source = disk['SOURCE'] + cut = "/#{disk['DATASTORE_ID']}/" + result = source.split(cut) + + if result.length == 2 + path = result[0] + else + path = '' + 0.upto(result.length - 2) do |i| + path << "#{result[i]}/#{dsid}/" + end + + path = path[0..path.rindex(cut)] + end + + path.gsub('//', '/') + end end # This class abstracts the access to XML elements. It provides basic methods diff --git a/src/vmm_mad/remotes/lxd/lxdrc b/src/vmm_mad/remotes/lxd/lxdrc index f6f1069ee0..90bffec6e1 100644 --- a/src/vmm_mad/remotes/lxd/lxdrc +++ b/src/vmm_mad/remotes/lxd/lxdrc @@ -27,20 +27,4 @@ :command: /bin/bash :width: 800 :height: 600 - :timeout: 300 - -################################################################################ -# OpenNebula Configuration Options -################################################################################ -# -# Default path for the datastores. This only need to be change if the -# corresponding value in oned.conf has been modified. -:datastore_location: /var/lib/one/datastores - - -################################################################################ -# LXD Options -################################################################################ -# -# Path to containers location to mount the root file systems -:containers: /var/lib/lxd/storage-pools/default/containers + :timeout: 300 \ No newline at end of file