026c379339
Entries in /sys/block for cciss look like cciss!c0d1 while the entry in /dev look like /dev/cciss/c0d1. udevadm uses the entry in /dev so we need to replace the ! to get a valid device name. More details here: https://review.openstack.org/#/c/61289/4/bin/agent Change-Id: I0eff17f45aa3fae0eada5b6e77bf63f6e93b61d3
149 lines
4.9 KiB
Ruby
149 lines
4.9 KiB
Ruby
# Copyright 2013 Mirantis, Inc.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
|
|
require "json"
|
|
require "base64"
|
|
require 'fileutils'
|
|
|
|
module MCollective
|
|
module Agent
|
|
class Erase_node < RPC::Agent
|
|
|
|
# Look at https://github.com/torvalds/linux/blob/master/Documentation/devices.txt
|
|
# Please also update the device codes here
|
|
# https://github.com/stackforge/fuel-web/blob/master/bin/agent#L43
|
|
STORAGE_CODES = [3, 8, 65, 66, 67, 68, 69, 70, 71, 104, 105, 106, 107, 108, 109, 110, 111, 202, 252, 253]
|
|
|
|
action "erase_node" do
|
|
erase_node
|
|
end
|
|
|
|
action "reboot_node" do
|
|
reboot
|
|
end
|
|
|
|
private
|
|
|
|
def erase_node
|
|
request_reboot = request.data[:reboot]
|
|
dry_run = request.data[:dry_run]
|
|
error_msg = []
|
|
reply[:status] = 0 # Shell exitcode behaviour
|
|
|
|
prevent_discover unless dry_run
|
|
|
|
tempfile_storage='/mnt/tempfiles'
|
|
|
|
begin
|
|
File.open('/proc/sys/kernel/panic','w') { |file| file.write("5\n") }
|
|
#Enable sysrq trigger for further hard reboot
|
|
File.open('/proc/sys/kernel/sysrq','w') { |file| file.write("1\n") }
|
|
get_boot_devices.each do |dev|
|
|
erase_data(dev[:name])
|
|
erase_data(dev[:name], 1, dev[:size], '512')
|
|
end unless dry_run
|
|
|
|
reply[:erased] = true
|
|
rescue Exception => e
|
|
reply[:erased] = false
|
|
reply[:status] += 1
|
|
msg = "MBR can't be erased. Reason: #{e.message};"
|
|
Log.error(msg)
|
|
error_msg << msg
|
|
end
|
|
|
|
begin
|
|
reboot if !dry_run && request_reboot
|
|
reply[:rebooted] = request_reboot
|
|
rescue Exception => e
|
|
reply[:rebooted] = false
|
|
reply[:status] += 1
|
|
msg = "Can't reboot node. Reason: #{e.message};"
|
|
Log.error(msg)
|
|
error_msg << "Can't reboot node. Reason: #{e.message};"
|
|
end
|
|
|
|
unless error_msg.empty?
|
|
reply[:error_msg] = error_msg.join(' ')
|
|
end
|
|
end
|
|
|
|
def get_boot_devices
|
|
raise "Path /sys/block does not exist" unless File.exists?("/sys/block")
|
|
Dir["/sys/block/*"].inject([]) do |blocks, block_device_dir|
|
|
basename_dir = File.basename(block_device_dir)
|
|
dev_name = basename_dir.gsub(/!/, '/')
|
|
major = `udevadm info --query=property --name=#{dev_name} | grep MAJOR`.strip.split(/\=/)[-1]
|
|
if File.exists?("/sys/block/#{basename_dir}/removable")
|
|
removable = File.open("/sys/block/#{basename_dir}/removable") { |f| f.read_nonblock(1024).strip }
|
|
end
|
|
if File.exists?("/sys/block/#{basename_dir}/size")
|
|
size = File.open("/sys/block/#{basename_dir}/size") { |f| f.read_nonblock(1024).strip }
|
|
else
|
|
size = 0
|
|
debug_msg("Can not define device size. File /sys/block/#{basename_dir}/size not found.")
|
|
end
|
|
|
|
if STORAGE_CODES.include?(major.to_i) && removable =~ /^0$/
|
|
blocks << {:name => dev_name, :size => size}
|
|
end
|
|
blocks
|
|
end
|
|
end
|
|
|
|
def reboot
|
|
pid = fork do
|
|
#Use sysrq trigger: Umount->Sync->reBoot
|
|
debug_msg("Run node rebooting command using 'SB' to sysrq-trigger")
|
|
sleep 5
|
|
# turning panic on oops and setting panic timeout to 10
|
|
File.open('/proc/sys/kernel/panic_on_oops', 'w') {|file| file.write("1\n")}
|
|
File.open('/proc/sys/kernel/panic','w') {|file| file.write("10\n")}
|
|
['b'].each do |req|
|
|
File.open('/proc/sysrq-trigger','w') do |file|
|
|
file.write("#{req}\n")
|
|
end
|
|
end
|
|
end
|
|
Process.detach(pid)
|
|
end
|
|
|
|
def erase_data(dev, length=1, offset=0, bs='1M')
|
|
cmd = "dd if=/dev/zero of=/dev/#{dev} bs=#{bs} count=#{length} seek=#{offset} oflag=direct"
|
|
status = system(cmd)
|
|
debug_msg("Run device erasing command '#{cmd}' returned '#{status}'")
|
|
|
|
status
|
|
end
|
|
|
|
# Prevent discover by agent while node rebooting
|
|
def prevent_discover
|
|
lock_path = '/var/run/nodiscover'
|
|
debug_msg("Create file for discovery preventing #{lock_path}")
|
|
FileUtils.touch(lock_path)
|
|
end
|
|
|
|
# In case of node erasing we can lose all
|
|
# debug messages, lets send them to orchestrator
|
|
def debug_msg(msg)
|
|
reply[:debug_msg] = [] unless reply[:debug_msg]
|
|
Log.debug(msg)
|
|
reply[:debug_msg] << msg
|
|
end
|
|
|
|
end
|
|
end
|
|
end
|