-
Notifications
You must be signed in to change notification settings - Fork 5
Add backup count option #4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
a57c2c6
cbfb497
e1cd86a
f2498b6
9ed531e
162356c
54b0f84
effe1a6
d932b97
940789f
7f5d74f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -13,6 +13,7 @@ | |
|
|
||
| import rados | ||
| import rbd | ||
| import sys | ||
|
|
||
| from rbd2qcow2.libc_wrappers import fallocate, FALLOC_FL_KEEP_SIZE | ||
| from rbd2qcow2.nbd_client import open_image | ||
|
|
@@ -130,7 +131,7 @@ async def do_transfer( | |
| os.fsync(xxx.fileno()) | ||
| log.debug('Fsyncing complete.') | ||
|
|
||
| os.chmod(tmp_filename, 0o400) | ||
| #os.chmod(tmp_filename, 0o400) | ||
| os.rename(tmp_filename, qcow2_name) | ||
| # Safe rename | ||
| fd = os.open(qcow2_directory, os.O_DIRECTORY | os.O_RDONLY) | ||
|
|
@@ -147,20 +148,18 @@ async def do_transfer( | |
| raise | ||
|
|
||
|
|
||
| def get_latest_backup(xxx: str, image_name: str) -> int: | ||
| def get_latest_backup(xxx: str, image_name: str)->dict: | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. я не уметь, питона в первый раз в руки взял
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. поэтому я и подсказываю |
||
| log.debug('Searching for previous images in backup dir.') | ||
| ts = 0 | ||
| ret = dict() | ||
| for filename in os.listdir(xxx): | ||
| mtch = re.fullmatch(r'([^@]+)@([0-9]+)\.qcow2', filename) | ||
| if mtch is None: | ||
| continue | ||
| if mtch.group(1) != image_name: | ||
| log.warning('Unexpected filename %r in dir %r.', filename, xxx) | ||
| continue | ||
| timestamp = int(mtch.group(2)) | ||
| if timestamp > ts: | ||
| ts = timestamp | ||
| return ts | ||
| ret[int(mtch.group(2))] = filename | ||
| return ret | ||
|
|
||
|
|
||
| def check_skip_backup(rbd_image) -> bool: | ||
|
|
@@ -196,7 +195,26 @@ async def do_backup(rbd_image_name: str, loop, ioctx): | |
| if not os.path.isdir(xxx): | ||
| log.debug('Creating directory %s.', xxx) | ||
| os.makedirs(xxx) | ||
| latest_ts = get_latest_backup(xxx, rbd_image_name) | ||
| itms = get_latest_backup(xxx, rbd_image_name) | ||
| cnt=len(itms) | ||
| srt=sorted(itms) | ||
| if cnt>0: | ||
| latest_ts=srt[-1] | ||
| else: | ||
| latest_ts=0 | ||
| if cnt >= options.bk_count: | ||
| removingCount = 1 + cnt - options.bk_count; | ||
| args = ['qemu-img', | ||
| 'rebase', | ||
| '-b', | ||
| os.path.join(xxx, itms[srt[1]]), | ||
| os.path.join(xxx, itms[srt[removingCount+2]]) | ||
| ]; | ||
| log.info('Rebasing image %s.', args) | ||
| subprocess.check_call(args) | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. если упадёт, то надо рековериться. ну и защита от параллельного бекапенья же в этом время
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Вот тут не знаю. Если ребейз упал, по идее, всей цепочке бекапов звездык |
||
| for idx in range(2, removingCount+2): | ||
| log.info('Removing old image %s.',os.path.join(xxx,itms[srt[idx]])) | ||
| os.remove(os.path.join(xxx,itms[srt[idx]])) | ||
| if latest_ts == 0: | ||
| log.info('Did not found previous backup for image %s.', rbd_image_name) | ||
| empty_image_path = os.path.join(xxx, 'empty.qcow2') | ||
|
|
@@ -220,6 +238,7 @@ async def do_backup(rbd_image_name: str, loop, ioctx): | |
| qcow2_name, | ||
| backing_store_filename, | ||
| ) | ||
|
|
||
| except Exception as e: | ||
| log.info('Removing RBD snapshot %s@%s due to error %r.', rbd_image_name, rbd_new_snapshot_name, | ||
| e) | ||
|
|
@@ -327,6 +346,14 @@ def main(): | |
| help='Path to a directory where backups should be placed.' | ||
| ) | ||
|
|
||
| parser.add_argument( | ||
| '--bk_count', | ||
| metavar='BK_COUNT', | ||
| type=int, | ||
| help='Count of backups to store.', | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. to store on Ceph (!) нет ?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. нит, то сторе он винт! |
||
| default=sys.maxsize | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. упоролся чтоли. по дефолту 1 же логичнее :)
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ну, тогда он будет держать ровно 1 бекап, остальные грохать
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. бля, я вначале думал -- сколько снапов в цефе будет оставаться. |
||
| ) | ||
|
|
||
| parser.add_argument( | ||
| 'images', | ||
| metavar='IMAGE_NAME', | ||
|
|
@@ -339,6 +366,9 @@ def main(): | |
| if not (1 <= options.parallel <= 100): | ||
| raise ValueError('Wrong parallel count.') | ||
|
|
||
| if not (options.bk_count >= 4): | ||
| raise ValueError('Wrong backups count. Minimum is 4.') | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. С чего это ради.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ну, если меньше четырех, то ребейсить некуда. Пушо получается, что самый первый бекап в цепочке он самый большой, поэтому на него ребейсить будет долго (наверное) |
||
|
|
||
| logging.basicConfig(level=logging.DEBUG if options.verbose else logging.INFO) | ||
|
|
||
| log.info('Starting backup process.') | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Но зачем? все генерируемые файлы должны быть ридонли. это защита от случайных правок (не по друтом которые)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Он же делает ребейс. Можно конечно покрасивше сделать - скидывать атрибут непосредственно перед ребейсом и после ребейса обратно ставить.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
может лутьше отдельную тулзу написать ? (т.е. не городить в этом питоне) -- бекапилка-бекапилкой, а удалялка тершака - отдельно. запускать их можно независимо в теории.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ну, тут сложна-сложна. По идее, это один процесс - хлопнуть один лишний бекап - добавить один новый бекап. Одна строка в кроне. Культурненько.