Created
July 2, 2014 20:11
-
-
Save jcpowermac/a69eb795c7ee01bdca38 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| require "fileutils" | |
| require "digest/sha2" | |
| module ProjectHanlon | |
| module ImageService | |
| # Base image abstract | |
| class Base < ProjectHanlon::Object | |
| MOUNT_COMMAND = (Process::uid == 0 ? "mount" : "sudo -n mount") | |
| UMOUNT_COMMAND = (Process::uid == 0 ? "umount" : "sudo -n umount") | |
| ARCHIVE_COMMAND = "fuseiso" | |
| ARCHIVE_UMOUNT_COMMAND = "fusermount" | |
| attr_accessor :filename | |
| attr_accessor :description | |
| attr_accessor :size | |
| attr_accessor :verification_hash | |
| attr_accessor :path_prefix | |
| attr_accessor :hidden | |
| def initialize(hash) | |
| super() | |
| @path_prefix = "base" | |
| @_namespace = :images | |
| @noun = "image" | |
| @description = "Image Base" | |
| @hidden = true | |
| from_hash(hash) unless hash == nil | |
| end | |
| def set_lcl_image_path(lcl_image_path) | |
| @_lcl_image_path = lcl_image_path + "/" + @path_prefix | |
| end | |
| # Used to add an image to the service | |
| # Within each child class the methods are overridden for that child template | |
| def add(src_image_path, lcl_image_path, extra) | |
| set_lcl_image_path(lcl_image_path) | |
| begin | |
| create_imagepath_success = false | |
| create_mount_success = false | |
| # Get full path | |
| fullpath = File.expand_path(src_image_path) | |
| # Get filename | |
| @filename = File.basename(fullpath) | |
| logger.debug "fullpath: #{fullpath}" | |
| logger.debug "filename: #@filename" | |
| logger.debug "mount path: #{mount_path}" | |
| #handler = archive_handler? | |
| # Make sure file exists | |
| return cleanup_on_failure(create_mount_success, create_imagepath_success, "File '#{fullpath}' does not exist") unless File.exist?(fullpath) | |
| # Make sure it has an .iso extension | |
| return cleanup_on_failure(create_mount_success, create_imagepath_success, "File '#{fullpath}' is not an ISO") if @filename[-4..-1] != ".iso" | |
| # Determine if there is an existing image path for iso | |
| if is_image_path? | |
| ## Remove if there is | |
| remove_dir_completely(image_path) | |
| end | |
| ## Create image path | |
| unless create_image_path | |
| logger.error "Cannot create image path: '#{image_path}'" | |
| return cleanup_on_failure(create_mount_success, create_imagepath_success, "Cannot create image path: '#{image_path}'") | |
| end | |
| ## if we reach here the creating of the image path is true | |
| create_imagepath_success = true | |
| create_mount_success = mount(fullpath) | |
| # unless create_mount_success | |
| # logger.error "Could not mount '#{fullpath}' on '#{mount_path}'" | |
| # return cleanup_on_failure(create_mount_success, create_imagepath_success, "Could not mount '#{fullpath}' on '#{mount_path}'",handler) | |
| # end | |
| # Copying mounted iso image to image_path | |
| FileUtils.cp_r(mount_path + "/.", image_path) | |
| unless verification | |
| return cleanup_on_failure(create_mount_success, create_imagepath_success,"Image copy failed verification: #{@verification_hash}") | |
| end | |
| rescue => e | |
| logger.error e.message | |
| return cleanup_on_failure(create_mount_success, create_imagepath_success, e.message) | |
| end | |
| umount | |
| [true, ''] | |
| end | |
| # Used to remove an image to the service | |
| # Within each child class the methods are overridden for that child template | |
| def remove(lcl_image_path) | |
| set_lcl_image_path(lcl_image_path) unless @_lcl_image_path != nil | |
| remove_dir_completely(image_path) | |
| !File.directory?(image_path) | |
| end | |
| # Verify diff between mount / image paths | |
| # For speed/flexibility reasons we just verify all files exists and not their contents | |
| def verification | |
| @verification_hash = get_dir_hash(image_path) | |
| src_hash = get_dir_hash(mount_path) | |
| unless src_hash == @verification_hash | |
| logger.error "Image copy failed verification: #{@verification_hash} <> #{src_hash}" | |
| return false | |
| end | |
| return true | |
| end | |
| # if a non-root method exists use it to extract the ISO, | |
| # if not fall back to mount and/or sudo mount | |
| # sudo -n | |
| def archive_handler? | |
| if !(cmd_path = `which #{ARCHIVE_COMMAND}`.strip).empty? && File.executable?(cmd_path) | |
| true | |
| elsif `#{MOUNT_COMMAND}` && $? == 0 | |
| false | |
| else | |
| raise "Neither #{ARCHIVE_COMMAND} or #{MOUNT_COMMAND} was available for extracting the ISO." | |
| end | |
| end | |
| def mount(src_image_path) | |
| FileUtils.mkpath(mount_path) unless File.directory?(mount_path) | |
| `#{ARCHIVE_COMMAND} #{src_image_path} #{mount_path} 2> /dev/null` | |
| if $? != 0 | |
| `#{MOUNT_COMMAND} -o loop #{src_image_path} #{mount_path} 2> /dev/null` | |
| if $? != 0 | |
| cleanup_on_failure(false, true, "Could not mount '#{fullpath}' on '#{mount_path}'") | |
| raise "Neither #{ARCHIVE_COMMAND} or #{MOUNT_COMMAND} was available for extracting the ISO." | |
| end | |
| end | |
| true | |
| # if handler | |
| # `#{ARCHIVE_COMMAND} #{src_image_path} #{mount_path} 2> /dev/null` | |
| # else | |
| # `#{MOUNT_COMMAND} -o loop #{src_image_path} #{mount_path} 2> /dev/null` | |
| # end | |
| # | |
| # if $? == 0 | |
| # logger.debug "mounted: #{src_image_path} on #{mount_path}" | |
| # true | |
| # else | |
| # logger.debug "could not mount: #{src_image_path} on #{mount_path}" | |
| # remove_dir_completely(mount_path) | |
| # false | |
| # end | |
| end | |
| # Used to verify an image within the filesystem (local/remote/possible Glance) | |
| # Within each child class the methods are overridden for that child emplate | |
| def verify(lcl_image_path) | |
| set_lcl_image_path(lcl_image_path) unless @_lcl_image_path != nil | |
| get_dir_hash(image_path) == @verification_hash | |
| end | |
| def image_path | |
| @_lcl_image_path + "/" + @uuid | |
| end | |
| def is_mounted?(src_image_path) | |
| mounts.each do | |
| |mount| | |
| return true if mount[0] == src_image_path && mount[1] == mount_path | |
| end | |
| false | |
| end | |
| def umount | |
| `#{ARCHIVE_UMOUNT_COMMAND} -u #{mount_path} 2> /dev/null` | |
| if $? != 0 | |
| `#{UMOUNT_COMMAND} #{mount_path} 2> /dev/null` | |
| if $? != 0 | |
| cleanup_on_failure(true, true, "Could not mount '#{fullpath}' on '#{mount_path}'") | |
| raise "Neither #{ARCHIVE_UMOUNT_COMMAND} or #{UMOUNT_COMMAND} was available for unmounting the ISO." | |
| else | |
| return :mount | |
| end | |
| end | |
| :fuseiso | |
| # if handler | |
| # `#{ARCHIVE_UMOUNT_COMMAND} -u #{mount_path} 2> /dev/null` | |
| # else | |
| # `#{UMOUNT_COMMAND} #{mount_path} 2> /dev/null` | |
| # end | |
| # | |
| # if $? == 0 | |
| # logger.debug "unmounted: #{mount_path}" | |
| # remove_dir_completely(mount_path) | |
| # true | |
| # else | |
| # logger.debug "could not unmount: #{mount_path}" | |
| # false | |
| # end | |
| end | |
| def mounts | |
| `#{MOUNT_COMMAND}`.split("\n").map! {|x| x.split("on")}.map! {|x| [x[0],x[1].split(" ")[0]]} | |
| end | |
| ## cleanup_on_failure method, based on arguments will unmount the iso, | |
| ## delete the mount directory and/or remove the image directory. | |
| ## If the image directory is removed a exception will be raised to inform | |
| ## the client of the error. | |
| def cleanup_on_failure(do_unmount,do_image_delete,errormsg) | |
| # unmount, if mounted | |
| # remove directory if created | |
| logger.error "Error: #{errormsg}" | |
| if do_unmount | |
| umount | |
| end | |
| if do_image_delete | |
| remove_dir_completely(image_path) | |
| raise "Deleted Image Directory, Image Path: #{image_path}, errormsg: #{errormsg}" | |
| end | |
| return [false,errormsg] | |
| end | |
| def mount_path | |
| "#{$temp_path}/#{@uuid}" | |
| end | |
| def is_image_path? | |
| File.directory?(image_path) | |
| end | |
| def create_image_path | |
| FileUtils.mkpath(image_path) | |
| end | |
| def remove_dir_completely(path) | |
| if File.directory?(path) | |
| FileUtils.rm_r(path, :force => true) | |
| else | |
| true | |
| end | |
| end | |
| def get_dir_hash(dir) | |
| logger.debug "Generating hash for path: #{dir}" | |
| files_string = Dir.glob("#{dir}/**/*").map {|x| x.sub("#{dir}/","")}.sort.join("\n") | |
| Digest::SHA2.hexdigest(files_string) | |
| end | |
| def print_header | |
| return "UUID", "Type", "ISO Filename", "Path", "Status" | |
| end | |
| def print_items | |
| set_lcl_image_path(ProjectHanlon.config.image_path) | |
| success, message = verify(@_lcl_image_path) | |
| return @uuid, @description, @filename, image_path.to_s, "#{success ? "Valid".green : "Broken/Missing".red}" | |
| end | |
| def print_item_header | |
| return "UUID", "Type", "ISO Filename", "Path", "Status" | |
| end | |
| def print_item | |
| set_lcl_image_path(ProjectHanlon.config.image_path) | |
| success, message = verify(@_lcl_image_path) | |
| return @uuid, @description, @filename, image_path.to_s, "#{success ? "Valid".green : "Broken/Missing".red}" | |
| end | |
| def line_color | |
| :white_on_black | |
| end | |
| def header_color | |
| :red_on_black | |
| end | |
| end | |
| end | |
| end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment