diff --git a/app/controllers/disk_service_controller.rb b/app/controllers/disk_service_controller.rb index 0d380e0..d2b7bb3 100644 --- a/app/controllers/disk_service_controller.rb +++ b/app/controllers/disk_service_controller.rb @@ -45,14 +45,4 @@ def operations_progress message = Device.progress_message(Device.progress) render json: {percentage: Device.progress, message: message} end - - # Render progress page when click on apply button in 'confirmation' step - # Implicitly send HTTP POST request to process_disk action to start processing the operations - # Expected key:values in @params: - # :debug => Integer value(1) if debug mode has selected in fourth step(confirmation), else nil - def progress - debug_mode = params[:debug] - self.user_selections = {debug: debug_mode} - end - end diff --git a/app/controllers/disk_wizard_controller.rb b/app/controllers/disk_wizard_controller.rb index dcc0b51..26e455a 100644 --- a/app/controllers/disk_wizard_controller.rb +++ b/app/controllers/disk_wizard_controller.rb @@ -58,10 +58,23 @@ def manage_disk def confirmation option = params[:option] label = params[:label].blank? ? nil : params[:label] + if label.size > 11 and user_selections['fs_type'].eql?(3) + redirect_to disk_wizards_engine.manage_path, :flash => {:error => "label have to be less than 12 character in FAT32"} + return false + end self.user_selections = {option: option, label: label} @selected_disk = Device.find(user_selections['path']) end + # Render progress page when click on apply button in 'confirmation' step + # Implicitly send HTTP POST request to process_disk action to start processing the operations + # Expected key:values in @params: + # :debug => Integer value(1) if debug mode has selected in fourth step(confirmation), else nil + def progress + debug_mode = params[:debug] + self.user_selections = {debug: debug_mode} + end + # An AJAX call is made to this action, from process.html.erb to start processing the queue # Create operation queue according to user selections(user_selections), and enqueue operations def process_disk diff --git a/app/models/device.rb b/app/models/device.rb index b66046d..e5cb028 100644 --- a/app/models/device.rb +++ b/app/models/device.rb @@ -2,6 +2,10 @@ # inheriting ActiveRecord::Base is not necessary class Device #< ActiveRecord::Base include Operation + + # 2 Tera byte is the edge between MBR and GPT + GPT_EDGE = 2 * 1024 * 1024 * 1024 * 1024 + # Device attributes: # vendor : Device vendor i.e. Western Digital Technologies # model : Device model i.e. WDBLWE0120JCH @@ -75,17 +79,27 @@ def create_partition(size = nil, type = Partition.PartitionType[:TYPE_PRIMARY]) end # TODO: Take partition table type as an input parameter , set default to MSDOS - def create_partition_table - DiskUtils.create_partition_table self + def create_partition_table type = 'msdos' + DiskUtils.create_partition_table self, type end def format_job params_hash DebugLogger.info "|#{self.class.name}|>|#{__method__}|:Params_hash #{params_hash}" new_fstype = params_hash[:fs_type] Device.progress = 10 - DebugLogger.info "|#{self.class.name}|>|#{__method__}|:Create partition_table #{partition_table}" #TODO: check the disk size and pass the relevent partition table type (i.e. if device size >= 3TB create GPT table else MSDOS(MBR)) - create_partition_table unless partition_table + DebugLogger.info "|#{self.class.name}|>|#{__method__}|:partition_table is '#{partition_table}'" + table = partition_table + if table.eql? "unknown" or table.eql? nil + if self.size.to_i > GPT_EDGE + table_type = 'gpt' + else + table_type = 'msdos' + end + create_partition_table table_type + DebugLogger.info "|#{self.class.name}|>|#{__method__}|:Create new partition_table of type #{table_type}" + end + DebugLogger.info "|#{self.class.name}|>|#{__method__}|:Full format label #{params_hash[:label]}" full_format new_fstype, params_hash[:label] Device.progress = 40 diff --git a/config/routes.rb b/config/routes.rb index dd03c91..18bd8c2 100755 --- a/config/routes.rb +++ b/config/routes.rb @@ -11,7 +11,7 @@ get "get_all_devices" => 'disk_service#get_all_devices' get "check_label" => 'disk_service#check_label' get 'debug_info' => 'disk_service#debug_info' - post 'process' => 'disk_service#progress' + post 'process' => 'disk_wizard#progress' get 'get_progress' => 'disk_service#operations_progress' match 'select' => 'disk_wizard#select_device',via: [:get,:post] diff --git a/lib/command_executor.rb b/lib/command_executor.rb index 5946282..98bdf47 100644 --- a/lib/command_executor.rb +++ b/lib/command_executor.rb @@ -55,7 +55,7 @@ def initialize command, parameters = nil # Execute the command with assigned parameters when initializing the object # == Parameters: # blocking is true =~ Command.run_now or blocking is not true =~ command.execute - def execute blocking = true, debug = @@debug_mode + def execute debug = @@debug_mode #If user select debug mode #1. push current command(command name and parameters) to `operations_log` array, where it will be used to list all the operations took place during the debug mode #2. Return from the method immediately,to prevent executing further @@ -74,15 +74,12 @@ def execute blocking = true, debug = @@debug_mode check root_folder script_location = File.join(root_folder, "elevated/") begin - if blocking - Open3.popen3("sudo", "./dsk-wz.sh", @command, @parameters, :chdir => script_location) do |stdin, stdout, stderr, wait_thr| - @stdout = stdout.read; @stderr = stderr.read; @wait_thr = wait_thr.value - end - else - _, @stdout, @stderr, @wait_thr = Open3.popen3("sudo", "./dsk-wz.sh", @command, @parameters, :chdir => script_location) + Open3.popen3("sudo", "./dsk-wz.sh", @command, @parameters, :chdir => script_location) do |stdin, stdout, stderr, wait_thr| + @stdout = stdout.read; @stderr = stderr.read; @wait_thr = wait_thr.value end rescue => error @success = false + DebugLogger.error "|#{self.class.name}|>|#{__method__}|:failed executing `#{@command}` Error is #{@stderr}" raise error end @exit_status = @wait_thr.exitstatus diff --git a/lib/disk_utils.rb b/lib/disk_utils.rb index a414e32..89f7e9d 100644 --- a/lib/disk_utils.rb +++ b/lib/disk_utils.rb @@ -19,6 +19,7 @@ class << self # Return an array of all the attached devices, including hard disks,flash/removable/external devices etc. # If search is given only search for the given path. def all_devices search = nil + DebugLogger.info "|#{self.class.name}|>|#{__method__}|:Search = #{search}" partitions = [] devices = [] device = nil @@ -29,9 +30,8 @@ def all_devices search = nil end lsblk = CommandExecutor.new command, params lsblk.execute - raise "Command execution error: #{lsblk.stderr.read}" if not lsblk.success? - lsblk.result.each_line.with_index do |line, idx| - next if idx == 0 + raise "Command execution error: #{lsblk.stderr}" if not lsblk.success? + lsblk.result.each_line do |line| data_hash = {} line.squish! line_data = line.gsub!(/"(.*?)"/, '\1#').split "#" @@ -99,8 +99,8 @@ def partition_type_hex kname params = " info --query=property --name=#{kname}" end udevadm = CommandExecutor.new command, params - udevadm.execute #false, false # None blocking and not debug mode - raise "Command execution error: #{udevadm.stderr.read}" if not udevadm.success? + udevadm.execute false # Not debug mode + raise "Command execution error: #{udevadm.stderr}" if not udevadm.success? udevadm.result.each_line do |line| line.squish! key = 'ID_PART_ENTRY_TYPE' @@ -117,8 +117,8 @@ def usage device end df = CommandExecutor.new command, params - df.execute #false, false # None blocking and not debug mode - raise "Command execution error: #{df.stderr.read}" if not df.success? + df.execute false # Not debug mode + raise "Command execution error: #{df.stderr}" if not df.success? line = df.result.lines.pop line.gsub!(/"/, '') df_data = line.split(" ") @@ -132,7 +132,7 @@ def partition_table device params = "-sm /dev/#{kname} unit b print free" # Use parted machine parseable output,independent from O/S language -s for --script and -m for --machine end parted = CommandExecutor.new command, params - parted.execute #false, false # None blocking and not debug mode + parted.execute false # Not debug mode return false if not parted.success? # REFERENCE: http://lists.alioth.debian.org/pipermail/parted-devel/2006-December/000573.html @@ -142,6 +142,41 @@ def partition_table device return table_type end + def label_partition partition_kname, label + filesystem = get_fs_partition partition_kname + case filesystem + when "ext3", "ext4", "ext2" + command = "e2label" + when "vfat" + # TODO: check prevent user to write label > 11 character in UI + command = "fatlabel" + when "ntfs" + command = "ntfslabel" + else + raise "this partititon filesystem '#{filesystem}' not supported" + end + + params = " /dev/#{partition_kname} '#{label}'" + label_pr = CommandExecutor.new command, params + label_pr.execute + raise "Command execution error: Add label = #{label}|message: #{label_pr.stderr}" if not label_pr.success? + end + + # Return the filesystem of partition + def get_fs_partition partition_kname + command = "blkid" + params = "-p -o export -u filesystem /dev/#{partition_kname}" + blkid = CommandExecutor.new command, params + blkid.execute + raise "Command execution error: #{blkid.stderr}" if not blkid.success? + blkid.result.each_line do |line| + line.strip!.chomp! + key, value = line.split('=') + return value if key.eql? "TYPE" + end + raise "Cannot find partition file system with this path /dev/#{partition_kname}" + end + def umount device #un-mounting not guaranteed, remain mounted if device is busy kname = get_kname device @@ -150,7 +185,7 @@ def umount device umount = CommandExecutor.new command, params #TODO: This should be a none-blocking call, until unmount the disk/device successfully, can't proceed with other works umount.execute - raise "Command execution error: #{umount.stderr.read}" if not umount.success? + raise "Command execution error: #{umount.stderr}" if not umount.success? end def mount mount_point, disk @@ -163,11 +198,11 @@ def mount mount_point, disk #remount all command = "mount" - params = "#{disk.path} #{mount_point}" + params = "#{disk.path} '#{mount_point}'" mount = CommandExecutor.new command, params DebugLogger.info "|#{self.class.name}|>|#{__method__}|:Mount executing" mount.execute - raise "Command execution error: #{mount.stderr.read}" if not mount.success? + raise "Command execution error: #{mount.stderr}" if not mount.success? end def format device, fstype @@ -191,7 +226,7 @@ def format device, fstype DebugLogger.info "|#{self.class.name}|>|#{__method__}|:Disk.kname = #{device.kname}, fstype = #{fstype} format params = #{params}" mkfs = CommandExecutor.new command, params mkfs.execute - raise "Command execution error: #{mkfs.stderr.read}" if not mkfs.success? + raise "Command execution error: #{mkfs.stderr}" if not mkfs.success? end #TODO: Need more testing @@ -200,7 +235,7 @@ def create_partition device, partition_type = 'primary', start_unit, end_unit params = "#{device.path} -s -a optimal unit MB mkpart #{partition_type} ext3 #{start_unit} -- #{end_unit}" parted = CommandExecutor.new command, params parted.execute - raise "Command execution error: #{parted.stderr.read}" if not parted.success? + raise "Command execution error: #{parted.stderr}" if not parted.success? probe_kernal device end @@ -210,7 +245,7 @@ def create_partition_table device, type = 'msdos' params = "--script #{device.path} mklabel #{type}" parted = CommandExecutor.new command, params parted.execute - raise "Command execution error: #{parted.stderr.read}" if not parted.success? + raise "Command execution error: #{parted.stderr}" if not parted.success? probe_kernal device #inform the OS of partition table changes end @@ -227,7 +262,7 @@ def delete_partition partition params = "--script #{device_path} rm #{partition.partition_number}" parted = CommandExecutor.new command, params parted.execute - raise "Command execution error: #{parted.stderr.read}" if not parted.success? + raise "Command execution error: #{parted.stderr}" if not parted.success? probe_kernal device_path end @@ -272,7 +307,7 @@ def get_path device blkid = CommandExecutor.new command, params DebugLogger.info "|#{self.class.name}|>|#{__method__}|:device = #{device.kname}, uuid = #{device.uuid}, params = #{params}" blkid.execute - raise "Command execution error:blkid error: #{blkid.stderr.read}" if not blkid.success? + raise "Command execution error:blkid error: #{blkid.stderr}" if not blkid.success? return blkid.result.lines.first.squish! end @@ -284,8 +319,8 @@ def get_parent child_path params = " info --query=property --name=#{child_path}" end udevadm = CommandExecutor.new command, params - udevadm.execute false, false # None blocking and not debug mode - raise "Command execution error: #{udevadm.stderr.read}" if not udevadm.success? + udevadm.execute false # Not debug mode + raise "Command execution error: #{udevadm.stderr}" if not udevadm.success? udevadm.result.each_line do |line| line.squish! key = 'ID_PART_ENTRY_DISK' @@ -299,7 +334,7 @@ def get_parent child_path end lsblk = CommandExecutor.new command, params lsblk.execute - raise "Command execution error: #{lsblk.stderr.read}" if not lsblk.success? + raise "Command execution error: #{lsblk.stderr}" if not lsblk.success? lsblk.result.each_line do |line| data_hash = {} line.squish! @@ -321,7 +356,7 @@ def clear_multipath multipath = CommandExecutor.new command, params if which command multipath.execute - raise "Command execution error: #{multipath.stderr}" if not multipath.success? + DebugLogger.error "|#{self.class.name}|>|#{__method__}|: Command execution error: #{multipath.stderr}" unless multipath.success? else return false end @@ -333,14 +368,15 @@ def get_partition_number partition_path params = " info --query=property --name=#{partition_path}" udevadm = CommandExecutor.new command, params udevadm.execute - raise "Command execution error: #{udevadm.stderr.read}" if not udevadm.success? + raise "Command execution error: #{udevadm.stderr}" if not udevadm.success? udevadm.result.each_line do |line| line.squish! key = 'ID_PART_ENTRY_NUMBER' _key, value = line.split '=' return value.to_i if _key.eql? key end - raise "Can't find partition number for #{partition_path} partition" + DebugLogger.error "Can't find partition number for #{partition_path} partition" + return data_hash['kname'].match(/[0-9]*$/)[0].to_i end #Quick `open3` wrapper for check availability of a system command, shows the full path of (shell) commands.Wrapper for linux 'which' command @@ -370,10 +406,10 @@ def get_kname device def create_directory location command = "mkdir" - params = "-p -m 757 #{location}" + params = "-p -m 757 '#{location}'" mkdir = CommandExecutor.new command, params mkdir.execute - raise "Command execution error: #{mkdir.stderr.read}" if not mkdir.success? + raise "Command execution error: #{mkdir.stderr}" if not mkdir.success? end def systemctl_wrapper systemd_name, action @@ -389,7 +425,7 @@ def systemctl_wrapper systemd_name, action end systemctl = CommandExecutor.new command, params systemctl.execute - raise "Command execution error: #{systemctl.stderr.read}" if not systemctl.success? + raise "Command execution error: #{systemctl.stderr}" if not systemctl.success? if action == 'show' _, description = systemctl.result.lines[0].squish!.split('=') _, active = systemctl.result.lines[1].squish!.split('=') diff --git a/lib/fstab.rb b/lib/fstab.rb index f6d57e7..fe1811a 100644 --- a/lib/fstab.rb +++ b/lib/fstab.rb @@ -80,12 +80,12 @@ def add_entry(opts = {}) params = " #{@contents} ! sudo tee /etc/fstab" echo = CommandExecutor.new command, params echo.execute - raise "Command execution error: #{echo.stderr.read}" if not echo.success? + raise "Command execution error: #{echo.stderr}" if not echo.success? command = "echo" params = " #{format_entry(dev, opts)} ! sudo tee -a /etc/fstab" echo = CommandExecutor.new command, params echo.execute - raise "Command execution error: #{echo.stderr.read}" if not echo.success? + raise "Command execution error: #{echo.stderr}" if not echo.success? reload end @@ -251,7 +251,7 @@ def self.get_blkdev_fs_attrs(dev) blkid = CommandExecutor.new command, params DebugLogger.info "|Fstab|>|#{__method__}|:device = #{dev}" blkid.execute - raise "Command execution error:blkid error: #{blkid.stderr.read}" if not blkid.success? + raise "Command execution error:blkid error: #{blkid.stderr}" if not blkid.success? blkid.result.each_line do |line| line.strip!.chomp! key, value = line.split('=') @@ -322,7 +322,7 @@ def backup_fstab echo = CommandExecutor.new command, params echo.execute - raise "Command execution error: #{echo.stderr.read}" if not echo.success? + raise "Command execution error: #{echo.stderr}" if not echo.success? # File.open("#{@backup_dir}/fstab.#{Time.now.to_f}.bak", 'w') do |f| # f.puts @contents # end diff --git a/lib/modules/operation.rb b/lib/modules/operation.rb index 7bed153..b2a72c0 100644 --- a/lib/modules/operation.rb +++ b/lib/modules/operation.rb @@ -1,5 +1,5 @@ module Operation - + DRIVE_MOUNT_ROOT = "/var/hda/files/drives" # Remove the partition from device/disk def delete #TODO: remove fstab entry if disk is permanently mounted @@ -9,9 +9,12 @@ def delete # Mount the partition with the given label, if no label is given kname will be used as default label def mount label + # TODO: Check if there is any device mounted with same mount_point self.reload label ||= self.kname - mount_point = File.join '/var/hda/files/drives/', label + DebugLogger.info "|#{self.class.name}|>|#{__method__}|:mount partition with label #{label} it's mountpoint is #{self.mountpoint}" + self.label_partition label + mount_point = File.join DRIVE_MOUNT_ROOT, label DiskUtils.mount mount_point, self end @@ -20,6 +23,13 @@ def unmount DiskUtils.umount self end + # Add label to partition + def label_partition label + raise "Cannot add label to a #{self.class.name} device" unless instance_of? Partition + DebugLogger.info "|#{self.class.name}|>|#{__method__}|:Add '#{label}' label to #{self.kname} partition" + DiskUtils.label_partition self.kname, label + end + # Format the partition to given file system type def format fstype if not Partition.FilesystemType.has_value?(fstype) @@ -37,7 +47,22 @@ def pre_checks_job params_hash # TODO: Implement rollback mechanism, if something went wrong bring back the system to original state,where it was before stating DW # TODO/Suggestion: Acquire a lock through 'flock()',for the device/partition involved. selected_element = Device.find params_hash[:path] - selected_element.unmount if (selected_element.instance_of? Partition and selected_element.mountpoint) + if selected_element.instance_of? Partition + #umount if the partition is mounted + if selected_element.mountpoint + selected_element.unmount + DebugLogger.info "|#{self.class.name}|>|#{__method__}|:umount partition from #{@mountpoint}" + end + else + #unmount all device partitions + #TODO: determind if the operation need to umount all partitions or not + selected_element.partitions.each do|partition| + if partition.mountpoint + DebugLogger.info "|#{self.class.name}|>|#{__method__}|: unmount partion dev/#{partition.kname}" + partition.unmount + end + end + end DiskUtils.clear_multipath end @@ -62,7 +87,7 @@ def reload DiskUtils.probe_kernal end DebugLogger.info "|#{self.class.name}|>|#{__method__}|:Device Kname #{self.kname}" - node = DiskUtils.find dev_path + node = (Device.find dev_path).as_json if node['type'].eql? 'part' node.each do |key, value| instance_variable_set("@#{key}", value) unless value.nil? diff --git a/lib/system_utils.rb b/lib/system_utils.rb index 8fd8e3e..6586dc7 100644 --- a/lib/system_utils.rb +++ b/lib/system_utils.rb @@ -25,7 +25,7 @@ def umount disk umount = CommandExecutor.new command, params #TODO: This should be a none-blocking call, until unmount the disk/device successfully, can't proceed with other works umount.execute - raise "Command execution error: #{umount.stderr.read}" if not umount.success? + raise "Command execution error: #{umount.stderr}" if not umount.success? end def mount mount_point, disk @@ -38,11 +38,11 @@ def mount mount_point, disk #remount all command = "mount" - params = "#{disk.path} #{mount_point}" + params = "#{disk.path} '#{mount_point}'" mount = CommandExecutor.new command, params DebugLogger.info "|#{self.class.name}|>|#{__method__}|:Mount executing" mount.execute - raise "Command execution error: #{mount.stderr.read}" if not mount.success? + raise "Command execution error: #{mount.stderr}" if not mount.success? end def probe_kernal device_path = nil @@ -55,7 +55,7 @@ def probe_kernal device_path = nil commands.each do |command, args| executor = CommandExecutor.new(command, args) executor.execute() - DebugLogger.info "Command execution error: #{executor.stderr.read}" if not executor.success? # Suppress warnings and errors,don't re-raise the exception.only do notify the kernel,Warnings and errors are out of the DW scope + DebugLogger.info "Command execution error: #{executor.stderr}" if not executor.success? # Suppress warnings and errors,don't re-raise the exception.only do notify the kernel,Warnings and errors are out of the DW scope end end @@ -79,7 +79,7 @@ def clear_multipath multipath = CommandExecutor.new command, params if which command multipath.execute - raise "Command execution error: #{multipath.stderr.read}" if not multipath.success? + DebugLogger.error "|#{self.class.name}|>|#{__method__}|: Command execution error: #{multipath.stderr}" unless multipath.success? else return false end @@ -87,10 +87,10 @@ def clear_multipath def create_directory location command = "mkdir" - params = "-p -m 757 #{location}" + params = "-p -m 757 '#{location}'" mkdir = CommandExecutor.new command, params mkdir.execute - raise "Command execution error: #{mkdir.stderr.read}" if not mkdir.success? + raise "Command execution error: #{mkdir.stderr}" if not mkdir.success? end def systemctl_wrapper systemd_name, action @@ -106,7 +106,7 @@ def systemctl_wrapper systemd_name, action end systemctl = CommandExecutor.new command, params systemctl.execute - raise "Command execution error: #{systemctl.stderr.read}" if not systemctl.success? + raise "Command execution error: #{systemctl.stderr}" if not systemctl.success? if action == 'show' _, description = systemctl.result.lines[0].squish!.split('=') _, active = systemctl.result.lines[1].squish!.split('=') diff --git a/vendor/bash/dsk-wz.sh b/vendor/bash/dsk-wz.sh index 2fa17d8..bd0e38a 100755 --- a/vendor/bash/dsk-wz.sh +++ b/vendor/bash/dsk-wz.sh @@ -9,11 +9,16 @@ write_log() while read text do if [ "$DEBUG_MODE" = true ]; then - LOGTIME=`date "+%Y-%m-%d %H:%M:%S"` - LOG="dsw.log" - touch $LOG - if [ ! -f $LOG ]; then echo "ERROR!! Cannot create log file $LOG. Exiting."; exit 1; fi - echo $LOGTIME": $text" | tee -a $LOG; + LOGTIME=`date "+%Y-%m-%d %H:%M:%S"` + LOG="dsw.log" + touch $LOG + if [ ! -f $LOG ]; then + message="ERROR!! Cannot create log file $LOG. Exiting."; + echo -e $message >&2; + echo $message | write_log; + exit -1; + fi + echo $LOGTIME": $text" >> $LOG; fi done } @@ -23,13 +28,15 @@ executor(){ shift; local params="$*"; echo "Start executing $command with arguments = $params" | write_log; - exec sudo $command $params #>> dsw.log 2>&1; + eval sudo $command $params; } if [ $# -lt 2 ] then - echo -e "An insufficient number of arguments(arguments)" | write_log ; - exit 1; + message="An insufficient number of arguments"; + echo -e $message >&2; + echo $message | write_log; + exit -1; fi command=$1; @@ -38,23 +45,17 @@ arguments="$@"; # Pattern matching Ref: http://goo.gl/JnXS5y case $command in -parted) - executor $command $arguments; -;; -mkfs.*) - executor $command $arguments; -;; -lsblk) - executor $command $arguments; -;; -fdisk) +parted | mkfs.* | lsblk | fdisk | df | udevadm | e2label | fatlabel | ntfslabel | blkid |\ + umount | mount | partprobe | echo | trigger | hdparm | multipath | mkdir | systemctl) executor $command $arguments; ;; + *) - executor $command $arguments; - # if a command is not one we know, we exit with an error - echo "Sorry, command $command is not known"; - exit 1; + # if a command is not one we know, we exit with an error + message="Sorry, command $command is not known"; + echo -e $message >&2; + echo $message | write_log; + exit -1; ;; esac -exit 1; \ No newline at end of file +exit 0;