#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import json
import datetime
import logging
import os
import subprocess
logging.basicConfig(level=logging.INFO,
format='%(asctime)s %(process)d %(thread)d %(levelname)s %(message)s @%(pathname)s:%(lineno)d')
def custom_subprocess_run(cmd):
logging.info('execute cmd %s' % cmd)
return subprocess.check_output(cmd, shell=True)
class DiskTool(object):
def __init__(self, prefix, skip_disk_with_mount=True):
self.skip_disk_with_mount = skip_disk_with_mount
self.prefix = prefix
@staticmethod
def ls_blk():
output = custom_subprocess_run("lsblk --json --output-all")
blk_info = json.loads(output)
return blk_info['blockdevices']
def find_mount_line_by_dev(self, dev):
mount_points = []
for disk in self.ls_blk():
if disk['kname'] == dev and disk.get('mountpoint'):
mount_points.append(disk['mountpoint'])
if disk.get('children'):
for part in disk['children']:
if part['kname'].startswith(dev) and part.get('mountpoint'):
mount_points.append(part['mountpoint'])
return mount_points
def is_dev_need_process(self, dev):
if dev.get('type') not in ('disk', 'part'):
# 只處理磁盤和分區(qū), 不處理loop設(shè)備等
logging.info("dev {} type is {}, no need process".format(dev['kname'], dev['type']))
return False
if dev.get('mountpoint') == '/': # 不處理系統(tǒng)盤
logging.info("dev {} mount /, no need process".format(dev['kname']))
return False
if dev['mountpoint'] and self.skip_disk_with_mount:
logging.info("dev {} mount {}, no need process".format(dev['kname'], dev['mountpoint']))
return False
return True
def find_disk_to_process(self):
ret = []
for disk in self.ls_blk():
if not self.is_dev_need_process(disk):
continue
for part in disk.get('children') or []:
if not self.is_dev_need_process(part):
break
else:
ret.append(disk)
return ret
@staticmethod
def get_disk_and_part_device_from_disks(disks):
ret = []
for disk in disks:
ret.append(disk.get('kname'))
for part in disk.get('children') or []:
ret.append(part.get('kname'))
return ret
@staticmethod
def format_dev(dev, label="ahaha"):
try_counter = 20
while try_counter > 0:
try:
try_counter -= 1
cmd = 'mkfs.ext4 -F -L {} /dev/{}'.format(label, dev)
return custom_subprocess_run(cmd)
except Exception as e:
logging.exception('format error: {}'.format(e))
raise
def get_uuids(self, disk_dev=None):
uuids = []
for dev in self.ls_blk():
if not disk_dev or dev['kname'] == disk_dev:
if dev['uuid']:
uuids.append(dev['uuid'])
if dev.get('children'):
for part in dev['children']:
uuids.append(part['uuid'])
return uuids
def is_fstab_line_valid(self, disk_dev, line):
line = line.strip()
items = [item.strip() for item in line.split()]
if not line:
return False
if items[1] == "/": # / mount reserved
return True
if line.startswith('#') or line.find(disk_dev) != -1:
return False
dev_uuids = self.get_uuids(disk_dev)
for uuid in dev_uuids:
if line.find(uuid) != -1:
return False
if items[0].startswith('UUID'): # clear invalid uuid
mount_uuid = items[0].split('=')[1]
blk_uuids = self.get_uuids()
if mount_uuid not in set(blk_uuids):
return False
for point in self.find_mount_line_by_dev(disk_dev):
if items[1].strip() == point:
return False
return True
def cleanup_fstab(self, disk_dev):
new_lines = []
with open('/etc/fstab', 'r') as f:
lines = f.readlines()
for line in lines:
if self.is_fstab_line_valid(disk_dev, line):
new_lines.append(line)
else:
logging.info("we remove fstab line {}".format(line))
with open('/etc/fstab', 'w') as f:
f.writelines(new_lines)
def add_disk_to_fstab(self, disk_dev, mountpoint):
uuids = self.get_uuids(disk_dev)
if len(uuids) > 1:
raise Exception("{} has partition, format failed".format(disk_dev))
with open('/etc/fstab', 'a') as f:
line = 'UUID={} {} ext4 nofail,noatime 0 2\n'.format(uuids[0], mountpoint)
logging.info('adding line to /etc/fstab, content=%s' % line.strip())
f.write(line)
def find_next_available_dataxx_mountpoint_from_fstab(self):
with open('/etc/fstab', 'r') as f:
fstab = f.read()
for i in range(0, 100):
mountpoint = self.prefix + str(i).zfill(2)
if mountpoint not in fstab:
logging.info('found available mount point from /etc/fstab mount point=%s' % mountpoint)
return mountpoint
return None
def umount_repartition_format_cleanup_fstab_of_disk(self, disk):
logging.info('processing disk.kname=%s', disk['kname'])
self.umount_disk([disk])
self.format_dev(disk['kname'])
self.cleanup_fstab(disk['kname'])
return disk['kname']
def umount_dev_if_mounted(self, dev):
mount_points = self.find_mount_line_by_dev(dev)
logging.info(mount_points)
if not mount_points:
return True
for mount in mount_points:
cmd = 'umount {}'.format(mount)
custom_subprocess_run(cmd)
def umount_disk(self, disks):
disk_and_disk_parts = self.get_disk_and_part_device_from_disks(disks)
for dev in disk_and_disk_parts:
self.umount_dev_if_mounted(dev)
@staticmethod
def backup_fstab():
now_str = datetime.datetime.now().strftime("%y.%m.%d_%H.%M.%S")
custom_subprocess_run("cp /etc/fstab /etc/fstab.{}.bak".format(now_str))
def remount_all(self):
self.backup_fstab()
data_disks = self.find_disk_to_process()
for disk in data_disks:
disk_dev = self.umount_repartition_format_cleanup_fstab_of_disk(disk)
mountpoint = self.find_next_available_dataxx_mountpoint_from_fstab()
if not os.path.exists(mountpoint):
os.makedirs(mountpoint)
self.add_disk_to_fstab(disk_dev, mountpoint)
custom_subprocess_run("mount -a")
def main():
# 如果磁盤已經(jīng)掛載默認(rèn)不作處理, 設(shè)置環(huán)境變量skip_disk_with_mount=yes后執(zhí)行,會(huì)卸載掉已經(jīng)掛載的盤進(jìn)行重新格式化&&掛載: 高危操作
skip_disk_with_mount = os.environ.get('skip_disk_with_mount', 'no') in ('true', 'yes', 'y', '1')
# 默認(rèn)掛載點(diǎn)以/data 作為前綴, 掛載成/data00捻艳、/data01勾给、/data99這樣的格式
prefix = os.environ.get("mount_prefix", "/data")
try:
disk_tool = DiskTool(prefix, skip_disk_with_mount)
disk_tool.remount_all()
except Exception as e:
logging.exception("do remount data disk faild: {}".format(str(e)))
if __name__ == '__main__':
main()
一鍵重新掛盤
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
- 文/潘曉璐 我一進(jìn)店門窿给,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人率拒,你說(shuō)我怎么就攤上這事填大。” “怎么了俏橘?”我有些...
- 文/不壞的土叔 我叫張陵允华,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我寥掐,道長(zhǎng)靴寂,這世上最難降的妖魔是什么? 我笑而不...
- 正文 為了忘掉前任召耘,我火速辦了婚禮百炬,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘污它。我一直安慰自己剖踊,他們只是感情好,可當(dāng)我...
- 文/花漫 我一把揭開白布衫贬。 她就那樣靜靜地躺著德澈,像睡著了一般。 火紅的嫁衣襯著肌膚如雪固惯。 梳的紋絲不亂的頭發(fā)上梆造,一...
- 那天,我揣著相機(jī)與錄音葬毫,去河邊找鬼镇辉。 笑死,一個(gè)胖子當(dāng)著我的面吹牛贴捡,可吹牛的內(nèi)容都是我干的忽肛。 我是一名探鬼主播,決...
- 文/蒼蘭香墨 我猛地睜開眼烂斋,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼屹逛!你這毒婦竟也來(lái)了础废?” 一聲冷哼從身側(cè)響起,我...
- 序言:老撾萬(wàn)榮一對(duì)情侶失蹤煎源,失蹤者是張志新(化名)和其女友劉穎色迂,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體手销,經(jīng)...
- 正文 獨(dú)居荒郊野嶺守林人離奇死亡歇僧,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
- 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了锋拖。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片诈悍。...
- 正文 年R本政府宣布舷夺,位于F島的核電站,受9級(jí)特大地震影響售貌,放射性物質(zhì)發(fā)生泄漏给猾。R本人自食惡果不足惜,卻給世界環(huán)境...
- 文/蒙蒙 一颂跨、第九天 我趴在偏房一處隱蔽的房頂上張望敢伸。 院中可真熱鬧,春花似錦恒削、人聲如沸池颈。這莊子的主人今日做“春日...
- 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)躯砰。三九已至,卻和暖如春斑粱,著一層夾襖步出監(jiān)牢的瞬間弃揽,已是汗流浹背。 一陣腳步聲響...
- 正文 我出身青樓尚揣,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親掖举。 傳聞我的和親對(duì)象是個(gè)殘疾皇子快骗,可洞房花燭夜當(dāng)晚...
推薦閱讀更多精彩內(nèi)容
- 滴滴在9月15日夜晚11點(diǎn)恢復(fù)了夜晚出行方篮,經(jīng)過一周時(shí)間的整改名秀,滴滴重新添加了“一鍵報(bào)警”功能。 打開滴滴app之后...
- 項(xiàng)目開發(fā)過程中,后臺(tái)的接口域名可能會(huì)分生產(chǎn)環(huán)境和測(cè)試環(huán)境巾表,生產(chǎn)環(huán)境是app正式環(huán)境汁掠,測(cè)試環(huán)境是開發(fā)過程中使用的環(huán)境...
- 上次我寫了一篇《親測(cè)SSD完美對(duì)拷攻略:電腦更換固態(tài)硬盤,無(wú)需重裝系統(tǒng)和軟件当娱!》吃既。?那篇文章是跟大伙分享我通過一次...
- 我是黑夜里大雨紛飛的人啊 1 “又到一年六月,有人笑有人哭趾访,有人歡樂有人憂愁态秧,有人驚喜有人失落,有的覺得收獲滿滿有...