├── .coveragerc ├── .gitignore ├── .gitreview ├── .pre-commit-config.yaml ├── .pylintrc ├── .stestr.conf ├── .zuul.yaml ├── CREDITS.rst ├── FAQ.rst ├── HACKING.rst ├── LICENSE ├── README.rst ├── bindep.txt ├── devstack ├── README.rst ├── gate_hook.sh ├── lib │ └── freezer ├── local.conf.example ├── plugin.sh └── settings ├── doc ├── .gitignore ├── README.rst ├── requirements.txt └── source │ ├── admin │ ├── agent-scheduler-install.rst │ ├── complete-install.rst │ ├── freezer-api-install.rst │ ├── freezer-web-ui-install.rst │ ├── index.rst │ ├── installation.rst │ └── operation.rst │ ├── cli │ ├── freezer-agent.rst │ ├── freezer-scheduler.rst │ └── index.rst │ ├── conf.py │ ├── contributor │ ├── actions.rst │ ├── api_documents.rst │ ├── api_routes.rst │ ├── client_structure.rst │ ├── index.rst │ ├── jobs.rst │ ├── metadata_structure.rst │ └── sessions.rst │ ├── images │ └── admin │ │ ├── Service_Architecture_02.png │ │ ├── freezer_agent_backup.png │ │ ├── freezer_agent_backup_api.png │ │ ├── freezer_dashboard.png │ │ ├── freezer_scheduler_api.png │ │ ├── freezer_scheduler_api_scale.png │ │ └── job_session.png │ ├── index.rst │ ├── install │ ├── common_configure.rst │ ├── common_prerequisites.rst │ ├── db-install.rst │ ├── devstack_plugin.rst │ ├── get_started.rst │ ├── index.rst │ ├── install-obs.rst │ ├── install-rdo.rst │ ├── install-ubuntu.rst │ ├── install.rst │ ├── install_agent.rst │ ├── next-steps.rst │ └── verify.rst │ ├── reference │ └── index.rst │ └── user │ ├── freezer-agent.rst │ ├── freezer-scheduler.rst │ ├── freezer-web-ui.rst │ ├── index.rst │ └── installation.rst ├── etc ├── config-generator.conf └── scheduler.conf.sample ├── freezer ├── __init__.py ├── common │ ├── __init__.py │ ├── client_manager.py │ └── config.py ├── engine │ ├── __init__.py │ ├── engine.py │ ├── glance │ │ ├── __init__.py │ │ └── glance.py │ ├── manager.py │ ├── nova │ │ ├── __init__.py │ │ └── nova.py │ ├── osbrick │ │ ├── __init__.py │ │ ├── brick_utils.py │ │ ├── client.py │ │ ├── osbrick.py │ │ └── volume_actions.py │ ├── rsync │ │ ├── __init__.py │ │ ├── pyrsync.py │ │ └── rsync.py │ ├── rsyncv2 │ │ ├── __init__.py │ │ ├── pyrsync.py │ │ └── rsyncv2.py │ └── tar │ │ ├── __init__.py │ │ ├── tar.py │ │ └── tar_builders.py ├── exceptions │ ├── __init__.py │ ├── engine.py │ └── utils.py ├── job.py ├── lib │ ├── __init__.py │ └── pep3143daemon │ │ ├── __init__.py │ │ ├── daemon.py │ │ └── pidfile.py ├── main.py ├── mode │ ├── __init__.py │ ├── cinder.py │ ├── cindernative.py │ ├── fs.py │ ├── glance.py │ ├── mode.py │ ├── mongo.py │ ├── mysql.py │ ├── nova.py │ └── sqlserver.py ├── openstack │ ├── __init__.py │ ├── admin.py │ ├── backup.py │ ├── osclients.py │ └── restore.py ├── scheduler │ ├── __init__.py │ ├── arguments.py │ ├── daemon.py │ ├── freezer_scheduler.py │ ├── scheduler_job.py │ ├── utils.py │ ├── win_daemon.py │ └── win_service.py ├── scripts │ └── vss.ps1 ├── snapshot │ ├── __init__.py │ ├── lvm.py │ ├── snapshot.py │ └── vss.py ├── storage │ ├── __init__.py │ ├── base.py │ ├── exceptions.py │ ├── fslike.py │ ├── ftp.py │ ├── local.py │ ├── multiple.py │ ├── physical.py │ ├── s3.py │ ├── ssh.py │ └── swift.py ├── tests │ ├── __init__.py │ ├── commons.py │ ├── integration │ │ ├── __init__.py │ │ ├── common.py │ │ ├── test_agent.py │ │ ├── test_rsync_backup.py │ │ └── test_version.py │ └── unit │ │ ├── __init__.py │ │ ├── engines │ │ ├── __init__.py │ │ ├── nova │ │ │ ├── __init__.py │ │ │ └── test_nova.py │ │ ├── rsync │ │ │ ├── __init__.py │ │ │ ├── test_pyrsync.py │ │ │ └── test_rsync.py │ │ └── tar │ │ │ ├── __init__.py │ │ │ └── test_tar_builders.py │ │ ├── openstack │ │ ├── __init__.py │ │ ├── test_admin.py │ │ ├── test_backup.py │ │ ├── test_osclients.py │ │ └── test_restore.py │ │ ├── scheduler │ │ ├── __init__.py │ │ ├── commons.py │ │ ├── test_freezer_scheduler.py │ │ ├── test_scheduler_daemon.py │ │ ├── test_scheduler_job.py │ │ └── test_utils.py │ │ ├── snapshot │ │ ├── __init__.py │ │ ├── test_lvm.py │ │ └── test_vss.py │ │ ├── storages │ │ ├── __init__.py │ │ ├── test_ftp.py │ │ └── test_local.py │ │ ├── test_job.py │ │ └── utils │ │ ├── __init__.py │ │ ├── test_checksum.py │ │ ├── test_config.py │ │ ├── test_crypt.py │ │ ├── test_exec_cmd.py │ │ ├── test_streaming.py │ │ ├── test_utils.py │ │ └── test_winutils.py └── utils │ ├── __init__.py │ ├── checksum.py │ ├── compress.py │ ├── config.py │ ├── crypt.py │ ├── exec_cmd.py │ ├── streaming.py │ ├── utils.py │ └── winutils.py ├── freezer_logo.jpg ├── releasenotes ├── notes │ ├── drop-py-2-7-a76d53b7a12bcff2.yaml │ ├── os-brick-engine-c47834de156dfa27.yaml │ ├── s3-driver-support-02d0a19b99cfe2c5.yaml │ └── volume-boot-nova-instance-backup-support-3c8d090370518f43.yaml └── source │ ├── 2023.1.rst │ ├── 2023.2.rst │ ├── 2025.1.rst │ ├── _static │ └── .placeholder │ ├── _templates │ └── .placeholder │ ├── conf.py │ ├── index.rst │ ├── newton.rst │ ├── ocata.rst │ ├── pike.rst │ ├── queens.rst │ ├── stein.rst │ ├── train.rst │ ├── unreleased.rst │ ├── ussuri.rst │ ├── victoria.rst │ ├── wallaby.rst │ ├── yoga.rst │ └── zed.rst ├── requirements.txt ├── setup.cfg ├── setup.py ├── test-requirements.txt └── tox.ini /.coveragerc: -------------------------------------------------------------------------------- 1 | # .coveragerc to control coverage.py 2 | [run] 3 | branch = True 4 | omit = freezer/tests/* 5 | 6 | [path] 7 | source = freezer/freezer 8 | 9 | [html] 10 | directory = term 11 | 12 | [report] 13 | ignore_errors = True 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | dist 3 | build 4 | .venv 5 | tests/scenario/.vagrant 6 | .idea 7 | .autogenerated 8 | .coverage 9 | cover/ 10 | coverage.xml 11 | *.sw? 12 | .tox 13 | *.egg 14 | *.egg-info 15 | *.py[co] 16 | .DS_Store 17 | *.log 18 | .stestr/ 19 | subunit.log 20 | .eggs 21 | AUTHORS 22 | ChangeLog 23 | 24 | # Django files that get created during the test runs 25 | .secret_key_store 26 | *.lock 27 | 28 | # Eclipse generates these 29 | .project 30 | .pydevproject 31 | 32 | # Coverage data 33 | .coverage.* 34 | releasenotes/build 35 | doc/source/_static/*.sample 36 | -------------------------------------------------------------------------------- /.gitreview: -------------------------------------------------------------------------------- 1 | [gerrit] 2 | host=review.opendev.org 3 | port=29418 4 | project=openstack/freezer.git 5 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | default_language_version: 3 | # force all unspecified python hooks to run python3 4 | python: python3 5 | repos: 6 | - repo: https://github.com/pre-commit/pre-commit-hooks 7 | rev: v3.4.0 8 | hooks: 9 | - id: trailing-whitespace 10 | - id: mixed-line-ending 11 | args: ['--fix', 'lf'] 12 | exclude: '.*\.(svg)$' 13 | - id: check-byte-order-marker 14 | - id: check-executables-have-shebangs 15 | - id: check-merge-conflict 16 | - id: debug-statements 17 | - id: check-yaml 18 | files: .*\.(yaml|yml)$ 19 | - repo: local 20 | hooks: 21 | - id: flake8 22 | name: flake8 23 | additional_dependencies: 24 | - hacking>=3.0.1,<3.1.0 25 | language: python 26 | entry: flake8 27 | files: '^.*\.py$' 28 | exclude: '^(doc|releasenotes|tools)/.*$' 29 | -------------------------------------------------------------------------------- /.stestr.conf: -------------------------------------------------------------------------------- 1 | [DEFAULT] 2 | test_path=${OS_TEST_PATH:-./freezer/tests/unit} 3 | top_dir=./ 4 | -------------------------------------------------------------------------------- /.zuul.yaml: -------------------------------------------------------------------------------- 1 | - project: 2 | queue: freezer 3 | templates: 4 | - check-requirements 5 | - publish-openstack-docs-pti 6 | - release-notes-jobs-python3 7 | - openstack-python3-jobs 8 | check: 9 | jobs: 10 | - openstack-tox-pylint 11 | - freezer-tempest-agent 12 | - freezer-tempest-scheduler 13 | - freezer-ubuntu 14 | - freezer-centos-9-stream 15 | gate: 16 | jobs: 17 | - openstack-tox-pylint 18 | - freezer-tempest-agent 19 | - freezer-tempest-scheduler 20 | - freezer-ubuntu 21 | - freezer-centos-9-stream 22 | 23 | - job: 24 | name: freezer-base 25 | parent: freezer-tempest-basic 26 | vars: 27 | tempest_test_regex: '^(freezer_tempest_plugin.tests.freezer)' 28 | 29 | - job: 30 | name: freezer-ubuntu 31 | parent: freezer-base 32 | 33 | - job: 34 | name: freezer-centos-9-stream 35 | parent: freezer-base 36 | nodeset: centos-9-stream 37 | -------------------------------------------------------------------------------- /CREDITS.rst: -------------------------------------------------------------------------------- 1 | Authors 2 | ======= 3 | 4 | - Fausto Marzi 5 | - Ryszard Chojnacki 6 | - Emil Dimitrov 7 | 8 | Maintainers 9 | =========== 10 | 11 | - Fausto Marzi 12 | - Ryszard Chojnacki 13 | - Emil Dimitrov 14 | - Geng changcai 15 | - Gecong 16 | - Carl Caihui 17 | 18 | Contributors 19 | ============ 20 | 21 | - Duncan Thomas 22 | - Coleman Corrigan 23 | - Guillermo Ramirez Garcia 24 | - Zahari Zahariev 25 | - Eldar Nugaev 26 | - Saad Zaher 27 | - Samuel Bartel 28 | - Jonas Pfannschmidt 29 | - Deklan Dieterly 30 | - Pierre Mathieu 31 | - Dmitriy Rabotyagov 32 | - Volodymyr Mevsha 33 | - Ivan Anfimov 34 | - Dmitriy Chubinidze 35 | 36 | Credits 37 | ======= 38 | 39 | - Davide Guerri 40 | - Jim West 41 | - Lars Noldan 42 | - Stephen Pearson 43 | - Sneha Mini 44 | - Chris Delaney 45 | - James Bishop 46 | - Matt Joyce 47 | - Anupriya Ramraj 48 | - HP OpsAuto Team 49 | - Jon Otero Fernández (logo) 50 | - ZTE TECS Team 51 | - Advanced Hosters B.V. 52 | - Napa Labs 53 | -------------------------------------------------------------------------------- /FAQ.rst: -------------------------------------------------------------------------------- 1 | === 2 | FAQ 3 | === 4 | 5 | 1) **What is freezer?** 6 | Is a tool to automate data backup and restore 7 | process using OpenStack Swift and/or other media storage. 8 | 9 | 2) **Does freezer support incremental backup?** 10 | Yes. Incremental backup are done using GNU tar incremental features. 11 | 12 | 3) **Does freezer check the file contents to establish if a file was modified?** 13 | Freezer check for changes at mtime and ctime in 14 | every file inode to evaluate if a file changed or not. 15 | 16 | 4) **Why GNU tar rather than rsync?** 17 | Both approaches are good. Rsync check 18 | the file content, while tar check the file inode. In our 19 | environment, we need to backup directories with size > 400GB and 20 | hundreds of thousands of files. Rsync approach is effective but slow. 21 | Tar is fast as it needs to check only the file inodes, rather than 22 | the full file content. Rsync backup type will be added pretty soon. 23 | 24 | 5) *Does freezer support encrypted backup?* 25 | Yes. Freezer encrypt data using OpenSSL (AES-256-CFB). 26 | 27 | 6) **Does freezer execute point-in-time backup?** 28 | Yes. For point in time backup LVM snapshot feature used. 29 | 30 | 7) **Can I use freezer on OSX or other OS where GNU Tar is not installed 31 | by default?** 32 | Yes. For OSX and \*BSD, just install gtar and freezer 33 | automatically will use gtar to execute backup. OS other then Linux, 34 | OSX and \*BSD are currently not supported. 35 | 36 | 8) **What Application backup does freezer support currently?** 37 | MongoDB, MySQL to have a higher level of data consistency, while 38 | any application is supported for crash consistent data integrity. 39 | 40 | 9) **How does the MongoDB backup happens?** 41 | Freezer required journal enabled in Mongo and lvm volume to execute backup. 42 | It checks if the Mongo instance is the master and create lvm snapshot to have 43 | consistent data. 44 | 45 | 10) **Does freezer manage sparse files efficiently?** 46 | Yes. Zeroed/null data is not backed up. So less space and bandwidth will be used. 47 | 48 | 11) **Does freezer remove automatically backup after some time?** 49 | Yes. From command line the option --remove-older-than (days) can be used to 50 | remove objects older then (days). 51 | 52 | 12) **Does freezer support MySQL Backup?** 53 | Yes. 54 | 55 | 13) **What storage media are currently supported?** 56 | Current support media storage are: 57 | a. Swift 58 | b. Store files on a remote host file system using ssh 59 | c. Directory in the local host (i.e. NFS/CIFS mounted volumes) 60 | 61 | 14) **Does freezer has any Web UI or API?** 62 | Yes. Freezer has REST API and a Web UI integrated with Horizon. 63 | 64 | 15) **Does Freezer detect removed files between incremental executions?** 65 | Yes. 66 | 67 | 16) **Will Freezer be included as official project in OpenStack?** 68 | Yes. 69 | 70 | 17) **Does freezer support Windows?** 71 | Yes. The freezer agent and scheduler can be executed on Windows. 72 | 73 | 18) **What is being used on Windows to execute file system snapshots?** 74 | Currently VSS are used to support point in time snapshots. 75 | 76 | 19) **What applications are supported in Windows for consistent backups?** 77 | SQL Server (--mode sqlserver). 78 | 79 | 20) **Are there examples of OpenStack projects that use Freezer that I can look at?** 80 | Not currently. 81 | 82 | 21) **My service has it's own task scheduler. What is the recommended way to schedule Freezer runs?** 83 | If you want to use the freezer-api and freezer-web-ui in horizon, you need to use the 84 | freezer-scheduler. 85 | If you do not need the api and web ui, you can just execute the freezer-agent from crontab or 86 | any other scheduler. 87 | 88 | 22) **What are the benefits of using the API and Web UI?** 89 | - You can start backup and restore jobs on any node from the Web UI 90 | - Backup jobs can be synchronized across multiple nodes 91 | - The UI provides metrics and other info 92 | 93 | 23) **How can I run user-defined scripts before and after a backup run?** 94 | A simple solution is to implement your own script locally and execute 95 | freezer-agent from it. 96 | We recommend instead creating "exec"-type jobs in the UI and set up job 97 | dependencies. 98 | 99 | 24) **What are the options for backing up MySQL/Percona?** 100 | a. Make use of filesystem snapshots (LVM) using the "--mode mysql" option. It's supported 101 | natively by Freezer and suitable for large databases. 102 | This instructs freezer to execute the following steps: 103 | - flush tables and lock the DB 104 | - take an LVM snapshot 105 | - release the DB (so it's usable again very quiclky) 106 | - backup the consistent DB image from the snapshot 107 | - release the snapshot 108 | b. Manual process - It does not require LVM and provides incremental backup. 109 | - manually flush tables and lock the DB: "flush tables with read lock" 110 | - backup the DB folder, usually /var/lib/mysql (--mode fs --path-to-backup /var/lib/mysql --max-level 14) 111 | - manually unlock the DB 112 | Using mysqldump is not recommended for speed and reliability reasons. 113 | -------------------------------------------------------------------------------- /HACKING.rst: -------------------------------------------------------------------------------- 1 | Freezer Style Commandments 2 | =========================== 3 | 4 | - Step 1: Read the OpenStack Style Commandments 5 | https://docs.openstack.org/hacking/latest/ 6 | - Step 2: Read on 7 | 8 | Freezer Specific Commandments 9 | ------------------------------ 10 | 11 | 12 | Logging 13 | ------- 14 | 15 | Use the common logging module, and ensure you ``getLogger``:: 16 | 17 | from oslo_log import log 18 | 19 | LOG = log.getLogger(__name__) 20 | 21 | LOG.debug('Foobar') 22 | 23 | 24 | oslo.config 25 | ----------- 26 | 27 | - All configuration options for freezer-scheduler should be in the following file :: 28 | 29 | freezer/scheduler/arguments.py 30 | 31 | - After adding new options to freezer-scheduler please use the following command to update the sample configuration file:: 32 | 33 | oslo-config-generator --config-file etc/config-generator.conf 34 | 35 | - If you added support for a new oslo library, you have to edit the following file adding a new namespace for the new oslo library: 36 | for example adding oslo.db:: 37 | 38 | # edit etc/config-generator.conf 39 | [DEFAULT] 40 | output_file = etc/scheduler.conf.sample 41 | wrap_width = 79 42 | namespace = scheduler 43 | namespace = oslo.log 44 | namespace = oslo.db 45 | 46 | This will add oslo.db options to your configuration file. 47 | 48 | Agent Options 49 | ------------- 50 | - All configuration options for freezer-agent should be in the following file :: 51 | 52 | freezer/common/config.py 53 | 54 | - To list options available in freezer-agent use the following command:: 55 | 56 | oslo-config-generator --namespace freezer --namespace oslo.log 57 | 58 | 59 | Release Notes 60 | ------------- 61 | 62 | - When a new feature is committed we should have a new release notes page that 63 | highlights the changes made. These release notes will be used for deployers, 64 | users, and developers. There are a few steps that need to be taken in order. 65 | 66 | * Generate a release notes page 67 | 1. tox -e venv -- reno new {my-new-feature} 68 | 2. https://docs.openstack.org/reno/latest/user/usage.html#creating-new-release-notes 69 | 70 | * Update/Edit an existing release note 71 | 1. https://docs.openstack.org/reno/latest/user/usage.html#editing-a-release-note 72 | 73 | 74 | - Building release notes:: 75 | 76 | tox -e releasenotes 77 | 78 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | ================= 2 | OpenStack Freezer 3 | ================= 4 | 5 | .. image:: freezer_logo.jpg 6 | 7 | Freezer is a Backup and Restore Service platform that helps you to automate 8 | the data backup and restore process. 9 | 10 | The following features are available: 11 | 12 | - Backup file system using point-in-time snapshot 13 | - Strong encryption supported: AES-256-CFB 14 | - Backup file system tree directly (without volume snapshot) 15 | - Backup journalled MongoDB directory tree using lvm snapshot to Swift 16 | - Backup MySQL with lvm snapshot 17 | - Restore data from a specific date automatically to file system 18 | - Low storage consumption as the backup are uploaded as a stream 19 | - Flexible backup policy (incremental and differential) 20 | - Data is archived in GNU Tar format for file based incremental 21 | - Multiple compression algorithm support (zlib, bzip2, xz) 22 | - Remove old backup automatically according to the provided parameters 23 | - Multiple storage media support (Swift, local file system, or ssh) 24 | - Flush kernel buffered memory to disk 25 | - Multi-platform (Linux, Windows, \*BSD, OSX) 26 | - Manage multiple jobs (I.e., multiple backups on the same node) 27 | - Synchronize backups and restore on multiple nodes 28 | - Web user interface integrated with OpenStack Horizon 29 | - Execute scripts/commands before or after a job execution 30 | - More ... 31 | 32 | To learn how to use Freezer's API, consult the documentation available online 33 | at: 34 | 35 | - `Backup API Reference `__ 36 | - `Freezer API `__ 37 | 38 | Freezer Horizon plugin: 39 | - `Freezer Web UI `__ 40 | 41 | For more information on OpenStack APIs, SDKs and CLIs in general, refer to: 42 | 43 | - `OpenStack for App Developers `__ 44 | - `Development resources for OpenStack clouds 45 | `__ 46 | 47 | Operators 48 | --------- 49 | 50 | To learn how to deploy and configure OpenStack Freezer, consult the 51 | documentation available online at: 52 | 53 | - `OpenStack Freezer `__ 54 | 55 | In the unfortunate event that bugs are discovered, they should be reported to 56 | the appropriate bug tracker. If you obtained the software from a 3rd party 57 | operating system vendor, it is often wise to use their own bug tracker for 58 | reporting problems. In all other cases use the master OpenStack bug tracker, 59 | available at: 60 | 61 | - `Bug Tracker `__ 62 | 63 | Troubleshooting 64 | --------------- 65 | 66 | When errors occure, these are good places to check: 67 | 68 | * freezer-api log: `$HOME/log/freezer-api.log` 69 | `/var/log/apache2/freezer-api.log` 70 | * freezer-agent log: `$HOME/.freezer/freezer.log` 71 | * freezer-scheduler log:`/var/log/freezer/scheduler.log` 72 | 73 | Developers 74 | ---------- 75 | 76 | Any new code must follow the development guidelines detailed in the HACKING.rst 77 | file and OpenStack general development guidelines, and pass all unit tests. 78 | 79 | Further developer focused documentation is available at: 80 | 81 | - `Official Freezer Documentation `__ 82 | - `Official Client Documentation 83 | `__ 84 | 85 | Contributors are encouraged to join IRC (``#openstack-freezer`` on OFTC): 86 | 87 | - `IRC `__ 88 | 89 | Other Information 90 | ----------------- 91 | 92 | Release notes for the project can be found at: 93 | 94 | - `Release notes 95 | `__ 96 | 97 | During each `Summit`_ and `Project Team Gathering`_, we agree on what the whole 98 | community wants to focus on for the upcoming release. The plans for freezer can 99 | be found at: 100 | 101 | - `Freezer Specs `__ 102 | 103 | .. _Summit: https://openinfra.org/summit/ 104 | .. _Project Team Gathering: https://openinfra.org/ptg/ 105 | -------------------------------------------------------------------------------- /bindep.txt: -------------------------------------------------------------------------------- 1 | # A build time dependency 2 | libffi-dev [platform:dpkg test] 3 | libffi-devel [platform:rpm test] 4 | libssl-dev [platform:dpkg test] 5 | openssl-devel [platform:rpm test] 6 | # these are needed to compile Python dependencies from sources 7 | python3-all-dev [platform:dpkg !platform:ubuntu-precise test] 8 | python3-devel [platform:rpm test] 9 | build-essential [platform:dpkg test] 10 | # these are needed by infra for python-* jobs 11 | mariadb [platform:rpm test] 12 | mariadb-server [platform:rpm test] 13 | mysql-client [platform:dpkg test] 14 | mysql-server [platform:dpkg test] 15 | -------------------------------------------------------------------------------- /devstack/README.rst: -------------------------------------------------------------------------------- 1 | ============================ 2 | Enabling Freezer in Devstack 3 | ============================ 4 | 5 | This directory contains the Freezer DevStack plugin. 6 | 7 | Download Devstack:: 8 | 9 | git clone https://git.openstack.org/openstack-dev/devstack 10 | cd devstack 11 | 12 | To configure the Freezer scheduler and agent with DevStack, you will need to 13 | enable this plugin by adding one line to the [[local|localrc]] 14 | section of your ``local.conf`` file. 15 | 16 | To enable the plugin, add a line of the form:: 17 | 18 | [[local|localrc]] 19 | enable_plugin freezer [GITREF] 20 | 21 | where:: 22 | 23 | is the URL of a freezer repository 24 | [GITREF] is an optional git ref (branch/ref/tag). The default is master. 25 | 26 | For example:: 27 | 28 | enable_plugin freezer https://git.openstack.org/openstack/freezer master 29 | 30 | Then run devstack normally:: 31 | 32 | cd $DEVICE_DIR 33 | ./stack.sh 34 | 35 | This is a sample ``local.conf`` file for freezer developer:: 36 | 37 | [[local|localrc]] 38 | ADMIN_PASSWORD=stack 39 | DATABASE_PASSWORD=stack 40 | RABBIT_PASSWORD=stack 41 | SERVICE_PASSWORD=$ADMIN_PASSWORD 42 | 43 | DEST=/opt/stack 44 | LOGFILE=$DEST/logs/stack.sh.log 45 | 46 | # only install keystone/horizon/swift in devstack 47 | # disable_all_services 48 | # enable_service key mysql s-proxy s-object s-container s-account horizon 49 | 50 | enable_plugin freezer http://git.openstack.org/openstack/freezer master 51 | enable_plugin freezer-api http://git.openstack.org/openstack/freezer-api.git master 52 | enable_plugin freezer-tempest-plugin http://git.openstack.org/openstack/freezer-tempest-plugin.git master 53 | enable_plugin freezer-web-ui http://git.openstack.org/openstack/freezer-web-ui.git master 54 | 55 | export FREEZER_BACKEND='sqlalchemy' 56 | 57 | For more information, see: 58 | https://docs.openstack.org/devstack/latest/index.html 59 | -------------------------------------------------------------------------------- /devstack/gate_hook.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # (c) Copyright 2014,2015 Hewlett-Packard Development Company, L.P. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | 16 | # set -ex 17 | 18 | echo "Start Gate Hook" 19 | # Link the log file so it will be collected by the CI system 20 | if [ -n "$BASE" ] && [ -d "$BASE/logs" ]; then 21 | sudo ln -sf /home/tempest/.freezer/freezer.log $BASE/logs/freezer.log 22 | sudo ln -sf $BASE/logs/freezer-scheduler.log $BASE/logs/freezersch.log 23 | fi 24 | 25 | 26 | 27 | #export DEVSTACK_LOCAL_CONFIG="enable_plugin freezer https://git.openstack.org/openstack/freezer" 28 | # export DEVSTACK_LOCAL_CONFIG+=$'\n'"enable_plugin freezer-api https://git.openstack.org/openstack/freezer-api" 29 | # Swift is needed for some of the integration tests 30 | export DEVSTACK_LOCAL_CONFIG+=$'\n'"enable_service s-proxy s-object s-container s-account" 31 | 32 | export DEVSTACK_GATE_TEMPEST_REGEX="freezer_tempest_plugin" 33 | 34 | # $BASE/new/devstack-gate/devstack-vm-gate.sh 35 | 36 | echo "End Gate Hook" 37 | -------------------------------------------------------------------------------- /devstack/lib/freezer: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # (c) Copyright 2014,2015 Hewlett-Packard Development Company, L.P. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | # Install and start Freezer service 18 | 19 | # add the following to localrc: 20 | # enable_service freezer 21 | # 22 | # Dependencies: 23 | # - functions 24 | # - OS_AUTH_URL for auth in api 25 | # - DEST set to the destination directory 26 | # - SERVICE_PASSWORD, SERVICE_TENANT_NAME for auth in api 27 | # - STACK_USER service user 28 | 29 | # functions called by the plugin.sh script 30 | # source plugin.sh [phase] 31 | # --------- 32 | # - 33 | # - [pre-install] 34 | # - [install] 35 | # - install_freezer 36 | # - [extra] 37 | # - init_freezer_scheduler 38 | # - start_freezer_scheduler 39 | # - 40 | # - stop_freezer_scheduler 41 | # - 42 | # - cleanup_freezer_scheduler 43 | 44 | # Save trace setting 45 | XTRACE=$(set +o | grep xtrace) 46 | set +o xtrace 47 | 48 | 49 | # Functions 50 | # --------- 51 | 52 | function is_freezer_enabled { 53 | [[ ,${ENABLED_SERVICES} =~ ,"freezer" ]] && return 0 54 | } 55 | 56 | 57 | # executed during: stack install 58 | function install_freezer { 59 | 60 | git_clone $FREEZER_REPO $FREEZER_DIR $FREEZER_BRANCH 61 | setup_develop $FREEZER_DIR 62 | if [[ "$GLOBAL_VENV" == "True" ]]; then 63 | sudo ln -sf /opt/stack/data/venv/bin/freezer /usr/local/bin 64 | sudo ln -sf /opt/stack/data/venv/bin/freezer-agent /usr/local/bin 65 | sudo ln -sf /opt/stack/data/venv/bin/freezer-scheduler /usr/local/bin 66 | fi 67 | } 68 | 69 | # executed during: stack post-config 70 | function configure_freezer_scheduler { 71 | 72 | [ ! -d $FREEZER_CONF_DIR ] && sudo mkdir -m 755 -p $FREEZER_CONF_DIR 73 | sudo cp -p $FREEZER_DIR/etc/scheduler.conf.sample $FREEZER_CONF_FILE 74 | sudo chown $STACK_USER $FREEZER_CONF_DIR 75 | 76 | # enable debug 77 | #iniset $FREEZER_CONF_FILE 'DEFAULT' debug True 78 | 79 | [ ! -d $FREEZER_JOBS_DIR ] && sudo mkdir -m 755 -p $FREEZER_JOBS_DIR 80 | sudo chown $STACK_USER $FREEZER_JOBS_DIR 81 | [ ! -d $FREEZER_LOG_DIR ] && sudo mkdir -m 755 -p $FREEZER_LOG_DIR 82 | sudo chown $STACK_USER $FREEZER_LOG_DIR 83 | sudo ls -lh $DEST 84 | sudo ls -lh $DEST/logs 85 | 86 | 87 | } 88 | 89 | 90 | # executed during: stack extra 91 | function init_freezer_scheduler { 92 | # Add scheduler settings here 93 | : 94 | } 95 | 96 | 97 | # executed during: stack extra 98 | function start_freezer_scheduler { 99 | # Add scheduler starts here 100 | : 101 | } 102 | 103 | 104 | # executed during: stop 105 | function stop_freezer_scheduler { 106 | stop_process freezer-scheduler 107 | } 108 | 109 | 110 | # utility function 111 | function get_id { 112 | echo `"$@" | awk '/ id / { print $4 }'` 113 | } 114 | 115 | # Restore xtrace 116 | $XTRACE 117 | -------------------------------------------------------------------------------- /devstack/local.conf.example: -------------------------------------------------------------------------------- 1 | # (c) Copyright 2014,2015 Hewlett-Packard Development Company, L.P. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | [[local|localrc]] 16 | disable_all_services 17 | 18 | enable_plugin freezer https://git.openstack.org/openstack/freezer master 19 | # To use a specific branch: 20 | # enable_plugin freezer https://git.openstack.org/openstack/freezer \ 21 | # stable/ 22 | 23 | enable_service rabbit mysql key 24 | 25 | # This is to keep the token small for testing 26 | KEYSTONE_TOKEN_FORMAT=UUID 27 | 28 | # Modify passwords as needed 29 | DATABASE_PASSWORD=secretdatabase 30 | RABBIT_PASSWORD=secretrabbit 31 | ADMIN_PASSWORD=secretadmin 32 | SERVICE_PASSWORD=secretservice 33 | SERVICE_TOKEN=111222333444 34 | 35 | -------------------------------------------------------------------------------- /devstack/plugin.sh: -------------------------------------------------------------------------------- 1 | # (c) Copyright 2014,2015 Hewlett-Packard Development Company, L.P. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # check for service enabled 16 | if is_service_enabled freezer; then 17 | if [[ "$1" == "source" || "`type -t install_freezer`" != 'function' ]]; then 18 | # Initial source 19 | source $FREEZER_DIR/devstack/lib/freezer 20 | fi 21 | 22 | if [[ "$1" == "stack" && "$2" == "install" ]]; then 23 | echo_summary "Installing Freezer" 24 | install_freezer 25 | elif [[ "$1" == "stack" && "$2" == "post-config" ]]; then 26 | echo_summary "Configuring Freezer" 27 | configure_freezer_scheduler 28 | elif [[ "$1" == "stack" && "$2" == "extra" ]]; then 29 | echo_summary "Initializing Freezer Scheduler" 30 | init_freezer_scheduler 31 | start_freezer_scheduler 32 | fi 33 | 34 | if [[ "$1" == "unstack" ]]; then 35 | stop_freezer_scheduler 36 | fi 37 | fi 38 | -------------------------------------------------------------------------------- /devstack/settings: -------------------------------------------------------------------------------- 1 | # (c) Copyright 2014,2015 Hewlett-Packard Development Company, L.P. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # Defaults 16 | # -------- 17 | 18 | # Set up default directories 19 | FREEZER_DIR=$DEST/freezer 20 | FREEZER_CONF_DIR=${FREEZER_CONF_DIR:-/etc/freezer} 21 | FREEZER_JOBS_DIR=${FREEZER_JOBS_DIR:-/etc/freezer/scheduler/conf.d} 22 | FREEZER_CONF_FILE=$FREEZER_CONF_DIR/scheduler.conf 23 | FREEZER_LOG_DIR=$DEST/logs 24 | 25 | # Freezer API repository 26 | FREEZER_REPO=${FREEZER_REPO:-${GIT_BASE}/openstack/freezer.git} 27 | FREEZER_BRANCH=${FREEZER_BRANCH:-master} 28 | 29 | enable_service freezer 30 | -------------------------------------------------------------------------------- /doc/.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | source/reference/api/ -------------------------------------------------------------------------------- /doc/requirements.txt: -------------------------------------------------------------------------------- 1 | # The order of packages is significant, because pip processes them in the order 2 | # of appearance. Changing the order has an impact on the overall integration 3 | # process, which may cause wedges in the gate later. 4 | 5 | sphinx>=2.0.0,!=2.1.0 # BSD 6 | sphinxcontrib-apidoc>=0.2.0 # BSD 7 | openstackdocstheme>=2.2.1 # Apache-2.0 8 | reno>=3.1.0 # Apache-2.0 9 | mock>=2.0.0 # BSD 10 | ddt>=1.0.1 # MIT 11 | testtools>=2.2.0 # MIT 12 | tempest>=17.1.0 # Apache-2.0 13 | -------------------------------------------------------------------------------- /doc/source/admin/agent-scheduler-install.rst: -------------------------------------------------------------------------------- 1 | Installation 2 | ============ 3 | 4 | Before Installation 5 | ------------------- 6 | 7 | - You will be required to install Freezer Agent before installing Freezer API 8 | or Freezer Web UI 9 | - Install Freezer Agent from source. Do not use pip! 10 | - Use this guide to install Freezer Agent to OpenStack Controller node 11 | (Where you have installed Horizon and Keystone) 12 | - Use corresponding release to your OpenStack version. For example; 13 | If your OpenStack version is Liberty, user stable/Liberty branch. 14 | 15 | Requirements 16 | ------------ 17 | 18 | - python 19 | - python-dev 20 | - git 21 | - Development Tools (gcc) 22 | - libffi 23 | - GNU Tar >= 1.26 24 | - gzip, bzip2, xz 25 | - OpenSSL 26 | - OpenSSL Development 27 | - python-swiftclient 28 | - python-keystoneclient 29 | - libmysqlclient-dev 30 | - sync 31 | 32 | You can check up to date required packages from "requirements.txt" 33 | 34 | Ubuntu / Debian Installation 35 | ---------------------------- 36 | 37 | **Follow these instructions if your OpenStack controller nodes are installed 38 | on Ubuntu or Debian based Linux distros** 39 | 40 | Install required packages first: 41 | 42 | .. code:: bash 43 | 44 | sudo apt-get install python-dev python-pip git openssl gcc make automake 45 | 46 | Clone proper branch of Freezer Client with git: 47 | 48 | .. code:: bash 49 | 50 | git clone -b [branch] https://github.com/openstack/freezer.git 51 | 52 | Install requirements with pip: 53 | 54 | .. code:: bash 55 | 56 | cd freezer/ 57 | 58 | sudo pip install -r requirements.txt 59 | 60 | Install freezer from source: 61 | 62 | .. code:: bash 63 | 64 | sudo python setup.py install 65 | -------------------------------------------------------------------------------- /doc/source/admin/complete-install.rst: -------------------------------------------------------------------------------- 1 | Installation 2 | ============ 3 | 4 | Before Installation 5 | ------------------- 6 | 7 | Requirements 8 | ------------ 9 | 10 | Ubuntu / Debian Installation 11 | ---------------------------- 12 | -------------------------------------------------------------------------------- /doc/source/admin/freezer-api-install.rst: -------------------------------------------------------------------------------- 1 | Installation 2 | ============ 3 | 4 | This guide will help you install Freezer API framework in one of your OpenStack 5 | controller node. You can install Freezer API in stand alone virtual or bare 6 | metal server but it is strongly suggested install in controller node. 7 | 8 | Before Installation 9 | ------------------- 10 | 11 | - Freezer Agent must be installed 12 | - Use this guide to install Freezer Agent to OpenStack Controller node 13 | (Where you have installed Horizon and Keystone) 14 | - Use corresponding release to your OpenStack version. For example; 15 | If your OpenStack version is Liberty, user stable/Liberty branch. 16 | - Do not forget to register Keystone API endpoint and service 17 | - Elasticsearch must be installed 18 | 19 | Requirements 20 | ------------ 21 | - elasticsearch>=1.3.0,<2.0 # Apache-2.0 22 | - falcon>=0.1.6 # Apache-2.0 23 | - jsonschema>=2.0.0,<3.0.0,!=2.5.0 # MIT 24 | - keystonemiddleware>=4.0.0 # Apache-2.0 25 | - oslo.config>=3.2.0 # Apache-2.0 26 | - oslo.i18n>=1.5.0 # Apache-2.0 27 | - Freezer Agent & Scheduler installed from source 28 | 29 | Ubuntu / Debian Installation 30 | ---------------------------- 31 | 32 | **Follow these instructions if your OpenStack controller nodes are installed 33 | on Ubuntu or Debian based Linux distros** 34 | 35 | Install required packages first: 36 | (If you have installed Freezer Agent from source, following packages are already installed.) 37 | 38 | .. code:: bash 39 | 40 | sudo apt-get install python-dev python-pip git openssl gcc make automake 41 | 42 | Clone proper branch of Freezer API with git: 43 | 44 | .. code:: bash 45 | 46 | git clone -b [branch] https://github.com/openstack/freezer-api.git 47 | 48 | Install requirements with pip: 49 | 50 | .. code:: bash 51 | 52 | cd freezer-api/ 53 | 54 | sudo pip install -r requirements.txt 55 | 56 | Install Freezer API from source: 57 | 58 | .. code:: bash 59 | 60 | sudo python setup.py install 61 | 62 | Copy config file: 63 | 64 | .. code:: bash 65 | 66 | sudo cp etc/freezer-api.conf /etc/freezer-api.conf 67 | 68 | Edit config file: 69 | 70 | .. code:: bash 71 | 72 | sudo nano /etc/freezer-api.conf 73 | 74 | # change log file location 75 | log_file = /var/log/freezer-api.log 76 | 77 | # configure Keystone authentication 78 | 79 | [keystone_authtoken] 80 | auth_protocol = http 81 | auth_host = [keystone_host_ip_or_hostname] 82 | auth_port = 5000 83 | admin_user = [freezer admin user] # admin or user with admin priviliges 84 | admin_password = [admin password] 85 | admin_tenant_name = [admin tenan] # usually admin 86 | include_service_catalog = False 87 | delay_auth_decision = False 88 | 89 | [storage] 90 | # supported db engine. currently elasticsearch only 91 | db=elasticsearch 92 | hosts='http://[elasticsearch host address]:9200' 93 | # freezer-manage db sync/update uses the following parameter to set the number of replicas 94 | number_of_replicas=1 95 | 96 | 97 | Follow this instructions to install Elasticsearch 1.7.5: 98 | 99 | .. code:: bash 100 | 101 | https://goo.gl/bwDcNK 102 | 103 | service elasticsearch start 104 | 105 | ***You must install Elasticsearch 1.7.5 for Freezer API to work correctly*** 106 | 107 | Elasticsearch needs to know what type of data each document's field contains. 108 | This information is contained in the "mapping", or schema definition. 109 | 110 | Elasticsearch will use dynamic mapping to try to guess the field type from the 111 | basic datatypes available in JSON, but some field's properties have to be 112 | explicitly declared to tune the indexing engine. 113 | 114 | Let's initialize database: 115 | 116 | .. code:: bash 117 | 118 | freezer-manage db sync 119 | 120 | Run Freezer API: 121 | 122 | .. code:: bash 123 | 124 | freezer-api 0.0.0.0 125 | 126 | There is not any Freezer API Deamon. If you need to run Freezer API in 127 | backgroun, user following commend: 128 | 129 | .. code:: bash 130 | 131 | freezer-api 0.0.0.0 >/dev/null 2>&1 132 | 133 | Keystone API v2.0 endpoint registration: 134 | 135 | .. code:: bash 136 | 137 | keystone service-create --name freezer --type backup \ 138 | --description "Freezer Backup Service" 139 | 140 | # use public IP address or hostname because Freezer Scheduler must be able 141 | to reach API from public IP or hostname. 142 | 143 | # default port is 9090. If you have changed in freezer-api.conf you must 144 | change it here too. 145 | 146 | keystone endpoint-create \ 147 | --service-id $(keystone service-list | awk '/ backup / {print $2}') \ 148 | --publicurl http://[freezer_api_publicurl]:[port] \ 149 | --internalurl http://[freezer_api_internalurl]:[port] \ 150 | --adminurl http://[freezer_api_adminurl]:[port] \ 151 | --region regionOne 152 | 153 | Keystone API v3 endpoint registration: 154 | 155 | .. code:: bash 156 | 157 | # With OpenStack Liberty, Keystone API v2.0 is depreciated and you will not 158 | able to use "keystone-client" commend instead user "openstack" commend 159 | 160 | openstack service create --name freezer \ 161 | --description "Freezer Backup Service" backup 162 | 163 | # use public IP address or hostname because Freezer Scheduler must be able 164 | to reach API from public IP or hostname. 165 | 166 | # default port is 9090. If you have changed in freezer-api.conf you must 167 | change it here too. 168 | 169 | openstack endpoint create --publicurl http://176.53.94.101:9090 \ 170 | --internalurl http://192.168.0.4:9090 \ 171 | --adminurl http://176.53.94.101:9090 \ 172 | --region RegionOne backup 173 | -------------------------------------------------------------------------------- /doc/source/admin/freezer-web-ui-install.rst: -------------------------------------------------------------------------------- 1 | Installation 2 | ============ 3 | 4 | Before Installation 5 | ------------------- 6 | 7 | Requirements 8 | ------------ 9 | 10 | Ubuntu / Debian Installation 11 | ---------------------------- 12 | -------------------------------------------------------------------------------- /doc/source/admin/index.rst: -------------------------------------------------------------------------------- 1 | Admin Guide 2 | =========== 3 | 4 | Table of Contents: 5 | 6 | .. toctree:: 7 | :maxdepth: 2 8 | 9 | installation 10 | operation 11 | -------------------------------------------------------------------------------- /doc/source/admin/installation.rst: -------------------------------------------------------------------------------- 1 | Installation 2 | ============ 3 | 4 | 5 | Agent & Scheduler Installation: 6 | ------------------------------- 7 | 8 | .. toctree:: 9 | :maxdepth: 2 10 | 11 | agent-scheduler-install 12 | 13 | Freezer API Installation 14 | ------------------------ 15 | 16 | .. toctree:: 17 | :maxdepth: 2 18 | 19 | freezer-api-install 20 | 21 | Web UI Installation 22 | ------------------- 23 | 24 | .. toctree:: 25 | :maxdepth: 2 26 | 27 | freezer-web-ui-install 28 | 29 | Backup as a Service Platform Installation 30 | ----------------------------------------- 31 | 32 | .. toctree:: 33 | :maxdepth: 2 34 | 35 | complete-install 36 | -------------------------------------------------------------------------------- /doc/source/admin/operation.rst: -------------------------------------------------------------------------------- 1 | Operation 2 | ========= 3 | 4 | Here goes operation guides... 5 | -------------------------------------------------------------------------------- /doc/source/cli/index.rst: -------------------------------------------------------------------------------- 1 | CLI Guide 2 | ========= 3 | 4 | .. toctree:: 5 | :maxdepth: 2 6 | 7 | freezer-agent 8 | freezer-scheduler 9 | 10 | 11 | This chapter assumes a working setup of OpenStack following the 12 | `OpenStack Installation Tutorial 13 | `_. 14 | -------------------------------------------------------------------------------- /doc/source/contributor/actions.rst: -------------------------------------------------------------------------------- 1 | Actions 2 | ======= 3 | 4 | Actions are stored only to facilitate the assembling of different actions into jobs in the web UI. 5 | They are not directly used by the scheduler. 6 | They are stored in this structure 7 | 8 | .. code-block:: none 9 | 10 | 11 | { 12 | 13 | "freezer_action": { 14 | "action": string, 15 | "backup_name": string, 16 | .... 17 | }, 18 | "mandatory": bool, 19 | "max_retries": int, 20 | "max_retries_interval": int 21 | 22 | "action_id": string, 23 | "user_id": string 24 | } 25 | 26 | -------------------------------------------------------------------------------- /doc/source/contributor/api_documents.rst: -------------------------------------------------------------------------------- 1 | API Documents 2 | ============= 3 | 4 | Freezer has different types of documents as follow: 5 | 6 | .. toctree:: 7 | 8 | jobs.rst 9 | actions.rst 10 | sessions.rst 11 | -------------------------------------------------------------------------------- /doc/source/contributor/client_structure.rst: -------------------------------------------------------------------------------- 1 | Freezer Client document structure 2 | ================================= 3 | 4 | Identifies a freezer client for the purpose of sending action 5 | 6 | client_info document contains information relevant for client identification 7 | 8 | .. code-block:: none 9 | 10 | client_info: 11 | { 12 | "client_id": string actually a concatenation "tenant-id_hostname" 13 | "hostname": string 14 | "description": string 15 | "uuid": 16 | } 17 | 18 | 19 | client_type document embeds the client_info and adds user_id 20 | 21 | .. code-block:: none 22 | 23 | client_type : 24 | { 25 | "client" : client_info document, 26 | "user_id": string, # owner of the information (OS X-User-Id, keystone provided, added by api) 27 | } 28 | -------------------------------------------------------------------------------- /doc/source/contributor/index.rst: -------------------------------------------------------------------------------- 1 | Developer Guide 2 | =============== 3 | 4 | .. toctree:: 5 | :maxdepth: 2 6 | 7 | api_routes 8 | metadata_structure 9 | client_structure 10 | api_documents 11 | 12 | -------------------------------------------------------------------------------- /doc/source/contributor/metadata_structure.rst: -------------------------------------------------------------------------------- 1 | Backup metadata structure 2 | ========================= 3 | 4 | .. note:: 5 | sizes are in MB 6 | 7 | .. code-block:: none 8 | 9 | backup_metadata:= 10 | { 11 | "container": string, 12 | "host_name": string, # fqdn, client has to provide consistent information here ! 13 | "backup_name": string, 14 | "time_stamp": int, 15 | "level": int, 16 | "max_level": int, 17 | "mode" : string, (fs mongo mysql) 18 | "fs_real_path": string, 19 | "vol_snap_path": string, 20 | "total_broken_links" : int, 21 | "total_fs_files" : int, 22 | "total_directories" : int, 23 | "backup_size_uncompressed" : int, 24 | "backup_size_compressed" : int, 25 | "compression_alg": string, (gzip bzip xz) 26 | "encrypted": bool, 27 | "client_os": string 28 | "broken_links" : [string, string, string], 29 | "excluded_files" : [string, string, string] 30 | "cli": string, equivalent cli used when executing the backup ? 31 | "version": string 32 | } 33 | 34 | 35 | The api wraps backup_metadata dictionary with some additional information. 36 | It stores and returns the information provided in this form 37 | 38 | .. code-block:: none 39 | 40 | { 41 | "backup_id": string # backup UUID 42 | "user_id": string, # owner of the backup metadata (OS X-User-Id, keystone provided) 43 | "user_name": string # owner of the backup metadata (OS X-User-Name, keystone provided) 44 | 45 | "backup_metadata": { #--- actual backup_metadata provided 46 | "container": string, 47 | "host_name": string, 48 | "backup_name": string, 49 | "timestamp": int, 50 | ... 51 | } 52 | } 53 | 54 | -------------------------------------------------------------------------------- /doc/source/contributor/sessions.rst: -------------------------------------------------------------------------------- 1 | Sessions 2 | ======== 3 | 4 | A session is a group of jobs which share the same scheduling time. A session is identified 5 | by its **session_id** and has a numeric tag (**session_tag**) which is incremented each time that a new session 6 | is started. 7 | The purpose of the *session_tag* is that of identifying a group of jobs which have been executed 8 | together and which therefore represent a snapshot of a distributed system. 9 | 10 | When a job is added to a session, the scheduling time of the session is copied into the 11 | job data structure, so that any job belonging to the same session will start at the same time. 12 | 13 | 14 | Session Data Structure 15 | ---------------------- 16 | 17 | .. code-block:: none 18 | 19 | session = 20 | { 21 | "session_id": string, 22 | "session_tag": int, 23 | "description": string, 24 | "hold_off": int (seconds), 25 | "schedule": { scheduling information, same as jobs }, 26 | "jobs": { 'job_id_1': { 27 | "client_id": string, 28 | "status": string, 29 | "result": string 30 | "time_started": int (timestamp), 31 | "time_ended": int (timestamp), 32 | }, 33 | 'job_id_2': { 34 | "client_id": string, 35 | "status": string, 36 | "result": string 37 | "time_started": int (timestamp), 38 | "time_ended": int (timestamp), 39 | } 40 | } 41 | "time_start": int timestamp, 42 | "time_end": int timestamp, 43 | "time_started": int (timestamp), 44 | "time_ended": int (timestamp), 45 | "status": string "completed" "running", 46 | "result": string "success" "fail", 47 | "user_id": string 48 | } 49 | 50 | Session actions 51 | --------------- 52 | 53 | When the freezer scheduler running on a node wants to start a session, 54 | it sends a POST request to the following endpoint: 55 | 56 | .. code-block:: none 57 | 58 | POST /v1/sessions/{sessions_id}/action 59 | 60 | The body of the request bears the action and parameters 61 | 62 | Session START action 63 | -------------------- 64 | 65 | .. code-block:: none 66 | 67 | { 68 | "start": { 69 | "job_id": "JOB_ID_HERE", 70 | "current_tag": 22 71 | } 72 | } 73 | 74 | Example of a successful response 75 | 76 | .. code-block:: none 77 | 78 | { 79 | 'result': 'success', 80 | 'session_tag': 23 81 | } 82 | 83 | Session STOP action 84 | ------------------- 85 | 86 | .. code-block:: none 87 | 88 | { 89 | "end": { 90 | "job_id": "JOB_ID_HERE", 91 | "current_tag": 23, 92 | "result": "success|fail" 93 | } 94 | } 95 | 96 | Session-Job association 97 | ----------------------- 98 | 99 | .. code-block:: rest 100 | 101 | PUT /v1/sessions/{sessions_id}/jobs/{job_id} adds the job to the session 102 | DELETE /v1/sessions/{sessions_id}/jobs/{job_id} adds the job to the session 103 | -------------------------------------------------------------------------------- /doc/source/images/admin/Service_Architecture_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/freezer/f0e435038467d1fe284ec4b8e87f70bfaa41f490/doc/source/images/admin/Service_Architecture_02.png -------------------------------------------------------------------------------- /doc/source/images/admin/freezer_agent_backup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/freezer/f0e435038467d1fe284ec4b8e87f70bfaa41f490/doc/source/images/admin/freezer_agent_backup.png -------------------------------------------------------------------------------- /doc/source/images/admin/freezer_agent_backup_api.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/freezer/f0e435038467d1fe284ec4b8e87f70bfaa41f490/doc/source/images/admin/freezer_agent_backup_api.png -------------------------------------------------------------------------------- /doc/source/images/admin/freezer_dashboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/freezer/f0e435038467d1fe284ec4b8e87f70bfaa41f490/doc/source/images/admin/freezer_dashboard.png -------------------------------------------------------------------------------- /doc/source/images/admin/freezer_scheduler_api.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/freezer/f0e435038467d1fe284ec4b8e87f70bfaa41f490/doc/source/images/admin/freezer_scheduler_api.png -------------------------------------------------------------------------------- /doc/source/images/admin/freezer_scheduler_api_scale.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/freezer/f0e435038467d1fe284ec4b8e87f70bfaa41f490/doc/source/images/admin/freezer_scheduler_api_scale.png -------------------------------------------------------------------------------- /doc/source/images/admin/job_session.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/freezer/f0e435038467d1fe284ec4b8e87f70bfaa41f490/doc/source/images/admin/job_session.png -------------------------------------------------------------------------------- /doc/source/install/common_prerequisites.rst: -------------------------------------------------------------------------------- 1 | Prerequisites 2 | ------------- 3 | 4 | #. Source the ``admin`` credentials to gain access to 5 | admin-only CLI commands: 6 | 7 | .. code-block:: console 8 | 9 | $ . admin-openrc 10 | 11 | #. To create the service credentials, complete these steps: 12 | 13 | * Create the ``freezer`` user: 14 | 15 | .. code-block:: console 16 | 17 | $ openstack user create --domain default --password-prompt freezer 18 | 19 | * Add the ``admin`` role to the ``freezer`` user: 20 | 21 | .. code-block:: console 22 | 23 | $ openstack role add --project service --user freezer admin 24 | 25 | * Create the freezer service entities: 26 | 27 | .. code-block:: console 28 | 29 | $ openstack service create --name freezer --description "Backup" backup 30 | 31 | #. Create the Backup service API endpoints: 32 | 33 | .. code-block:: console 34 | 35 | $ openstack endpoint create --region RegionOne \ 36 | backup public http://controller:9090/ 37 | $ openstack endpoint create --region RegionOne \ 38 | backup internal http://controller:9090/ 39 | $ openstack endpoint create --region RegionOne \ 40 | backup admin http://controller:9090/ 41 | -------------------------------------------------------------------------------- /doc/source/install/db-install.rst: -------------------------------------------------------------------------------- 1 | Install and configure database 2 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 3 | 4 | Before you install and configure the Backup/Restore service, 5 | you must install the database. 6 | 7 | #. To install elasticsearch on Ubuntu, complete these steps: 8 | 9 | * Install java prerequisites: 10 | 11 | .. code-block:: console 12 | 13 | $ sudo apt-get install default-jre-headless 14 | 15 | * Download ``elasticsearch`` version 2.3.0 16 | 17 | .. code-block:: console 18 | 19 | $ wget https://download.elasticsearch.org/elasticsearch/release/org/elasticsearch/distribution/deb/elasticsearch/2.3.0/elasticsearch-2.3.0.deb 20 | 21 | * Install ``elasticsearch`` 22 | 23 | .. code-block:: console 24 | 25 | $ sudo dpkg -i elasticsearch-2.3.0.deb 26 | $ sudo update-rc.d elasticsearch defaults 95 10 27 | 28 | 29 | 30 | #. To install elasticsearch on Fedora, complete these steps: 31 | 32 | * Install java prerequisites: 33 | 34 | .. code-block:: console 35 | 36 | $ sudo yum install java-1.8.0-openjdk-headless 37 | 38 | * Download ``elasticsearch`` version 2.3.0 39 | 40 | .. code-block:: console 41 | 42 | $ wget https://download.elasticsearch.org/elasticsearch/release/org/elasticsearch/distribution/rpm/elasticsearch/2.3.0/elasticsearch-2.3.0.rpm 43 | 44 | * Install ``elasticsearch`` 45 | 46 | .. code-block:: console 47 | 48 | $ sudo yum install elasticsearch-2.3.0.rpm 49 | -------------------------------------------------------------------------------- /doc/source/install/devstack_plugin.rst: -------------------------------------------------------------------------------- 1 | Devstack Plugin 2 | =============== 3 | 4 | Edit local.conf 5 | --------------- 6 | 7 | To configure the Freezer API with DevStack, you will need to enable the 8 | freezer-api plugin by adding one line to the [[local|localrc]] section 9 | of your local.conf file: 10 | 11 | .. code-block:: ini 12 | 13 | enable_plugin freezer-api [GITREF] 14 | enable_plugin freezer [GITREF] 15 | enable_plugin freezer-web-ui [GITREF] 16 | 17 | where 18 | 19 | .. code-block:: none 20 | 21 | is the URL of a freezer-api, freezer, freezer-web-ui repository 22 | [GITREF] is an optional git ref (branch/ref/tag). The default is master. 23 | 24 | For example 25 | 26 | .. code-block:: ini 27 | 28 | enable_plugin freezer-api https://git.openstack.org/openstack/freezer-api.git master 29 | enable_plugin freezer https://git.openstack.org/openstack/freezer.git master 30 | enable_plugin freezer-web-ui https://git.openstack.org/openstack/freezer-web-ui.git master 31 | 32 | Plugin Options 33 | -------------- 34 | 35 | The plugin makes use of apache2 by default. 36 | To use the *uwsgi* server set the following environment variable 37 | 38 | .. code-block:: bash 39 | 40 | export FREEZER_API_SERVER_TYPE=uwsgi 41 | 42 | The default port is *9090*. To configure the api to listen on a different port 43 | set the variable `FREEZER_API_PORT`. 44 | For example to make use of port 19090 instead of 9090 use 45 | 46 | .. code-block:: bash 47 | 48 | export FREEZER_API_PORT=19090 49 | 50 | For more information, see `openstack_devstack_plugins_install `_ 51 | -------------------------------------------------------------------------------- /doc/source/install/get_started.rst: -------------------------------------------------------------------------------- 1 | Backup/Restore service overview 2 | =============================== 3 | The Backup/Restore service provides an easy way to backup and restore 4 | your OpenStack workloads to different storage. 5 | 6 | The Backup and restore service consists of the following components: 7 | - freezer-api 8 | - freezer-agent 9 | - freezer-scheduler 10 | 11 | The service features a RESTful API, which can be used to maintain the status of 12 | your jobs, backups and metadata. 13 | 14 | This chapter assumes a working setup of OpenStack following the base 15 | Installation Guide. 16 | 17 | 18 | Concepts and definitions 19 | ======================== 20 | 21 | 22 | ``freezer-api`` service 23 | Accepts and responds to end user API calls. 24 | ``freezer-api`` service documentation can be found here: 25 | `Freezer API `_ 26 | 27 | 28 | ``freezer-scheduler`` service 29 | Does API calls to ``freezer-api`` to schedule, fetch, update or Delete backup 30 | jobs. 31 | 32 | 33 | ``freezer-agent`` service 34 | Python application run on the same node like ``freezer-scheduler`` and it 35 | gets called by ``freezer-scheduler`` to execute backups/restore operations. 36 | 37 | 38 | *hostname* is _probably_ going to be the host fqdn. 39 | 40 | *backup_id* defined as UUID of a backup. 41 | -------------------------------------------------------------------------------- /doc/source/install/index.rst: -------------------------------------------------------------------------------- 1 | Backup/Restore service 2 | ====================== 3 | 4 | .. toctree:: 5 | :maxdepth: 2 6 | 7 | get_started 8 | install 9 | verify 10 | next-steps 11 | 12 | This chapter assumes a working setup of OpenStack following the 13 | `OpenStack Installation Tutorial 14 | `_. 15 | -------------------------------------------------------------------------------- /doc/source/install/install-obs.rst: -------------------------------------------------------------------------------- 1 | Install and configure for openSUSE and SUSE Linux Enterprise 2 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 3 | 4 | This section describes how to install and configure the Backup service 5 | for openSUSE Leap 42.1 and SUSE Linux Enterprise Server 12 SP1. 6 | 7 | .. include:: common_prerequisites.rst 8 | 9 | Install and configure components 10 | -------------------------------- 11 | 12 | #. Install the packages: 13 | 14 | .. code-block:: console 15 | 16 | zypper --quiet --non-interactive install python-dev python-pip 17 | 18 | .. include:: common_configure.rst 19 | 20 | 21 | Finalize installation 22 | --------------------- 23 | 24 | Start the Backup services and configure them to start when 25 | the system boots: 26 | 27 | .. code-block:: console 28 | 29 | # systemctl enable openstack-freezer-api.service 30 | 31 | # systemctl start openstack-freezer-api.service 32 | -------------------------------------------------------------------------------- /doc/source/install/install-rdo.rst: -------------------------------------------------------------------------------- 1 | Install and configure for Red Hat Enterprise Linux and CentOS 2 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 3 | 4 | 5 | This section describes how to install and configure the Backup service 6 | for Red Hat Enterprise Linux 9 and CentOS Stream 9. 7 | 8 | .. include:: common_prerequisites.rst 9 | 10 | Install and configure components 11 | -------------------------------- 12 | 13 | #. Install the packages: 14 | 15 | .. code-block:: console 16 | 17 | $ sudo dnf install python3-devel python3-pip 18 | 19 | .. include:: common_configure.rst 20 | 21 | Finalize installation 22 | --------------------- 23 | 24 | Start the Backup services and configure them to start when 25 | the system boots: 26 | 27 | .. code-block:: console 28 | 29 | $ sudo systemctl enable openstack-freezer-api.service 30 | 31 | $ sudo systemctl start openstack-freezer-api.service 32 | -------------------------------------------------------------------------------- /doc/source/install/install-ubuntu.rst: -------------------------------------------------------------------------------- 1 | Install and configure for Ubuntu 2 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 3 | 4 | This section describes how to install and configure the Backup 5 | service for Ubuntu 16.04 (LTS). 6 | 7 | .. include:: common_prerequisites.rst 8 | 9 | Install and configure components 10 | -------------------------------- 11 | 12 | #. Install the packages: 13 | 14 | .. code-block:: console 15 | 16 | $ sudo apt-get update 17 | 18 | $ sudo apt-get install python-dev python-pip 19 | 20 | .. note:: To list all missing packages needed to install freezer 21 | in your system use provided ``bindep.txt`` file with `bindep utility. 22 | `_ 23 | 24 | 25 | .. include:: common_configure.rst 26 | 27 | Finalize installation 28 | --------------------- 29 | 30 | Restart the Backup services: 31 | 32 | .. code-block:: console 33 | 34 | $ sudo service openstack-freezer-api restart 35 | -------------------------------------------------------------------------------- /doc/source/install/install.rst: -------------------------------------------------------------------------------- 1 | Install and configure 2 | ~~~~~~~~~~~~~~~~~~~~~ 3 | 4 | This section describes how to install and configure the 5 | Backup service, code-named freezer-api, on the controller node. 6 | 7 | This section assumes that you already have a working OpenStack 8 | environment with at least the following components installed: 9 | .. Keystone 10 | 11 | Note that installation and configuration vary by distribution. 12 | 13 | .. toctree:: 14 | :maxdepth: 2 15 | 16 | db-install 17 | install-obs 18 | install-rdo 19 | install-ubuntu 20 | 21 | .. code-block:: console 22 | 23 | $ git clone https://opendev.org/openstack/freezer-api.git 24 | $ cd freezer-api 25 | $ pip install ./ 26 | 27 | 28 | .. toctree:: 29 | 30 | devstack_plugin.rst 31 | -------------------------------------------------------------------------------- /doc/source/install/install_agent.rst: -------------------------------------------------------------------------------- 1 | This section describes how to install and configure freezer-scheduler and 2 | freezer-agent, on any node in the cloud or any vm inside the cloud. 3 | 4 | This section assumes that you already have a working OpenStack 5 | environment with at least the following components installed: 6 | - Keystone 7 | - Swift 8 | 9 | .. code-block:: bash 10 | 11 | git clone https://git.openstack.org/openstack/freezer.git 12 | cd freezer 13 | pip install ./ 14 | 15 | 16 | Configure the scheduler 17 | ----------------------- 18 | 19 | 1. Copy the configuration files to ``/etc/freezer/``: 20 | 21 | 22 | .. code-block:: console 23 | 24 | $ sudo cp etc/scheduler.conf.sample /etc/freezer/scheduler.conf 25 | 26 | 27 | 2. Edit the ``/etc/freezer/scheduler.conf`` file and complete the following 28 | actions: 29 | 30 | There are two kinds of api interface, v1 and v2. 31 | 32 | There are two kinds of configurations: 33 | 34 | ``notes:`` 35 | 36 | Default configuration is freezer api v2. 37 | 38 | ``Configuration1``: freezer-api is started by v1 interface: 39 | 40 | * In the ``[DEFAULT]`` section, configure database access: 41 | 42 | The ``client_id`` has to be set to the hostname of the machine. It will be 43 | used as an identifier for this node to fetch its scheduled backups 44 | 45 | .. code-block:: ini 46 | 47 | [DEFAULT] 48 | ... 49 | client_id = hostname_of_machine 50 | jobs_dir = /etc/freezer/scheduler/conf.d 51 | enable_v1_api = True 52 | 53 | ``Configuration2``: freezer-api is started by v2 interface: 54 | 55 | * In the ``[DEFAULT]`` section, configure database access: 56 | 57 | The ``client_id`` has to be set to the hostname of the machine. It will be 58 | used as an identifier for this node to fetch its scheduled backups 59 | 60 | .. code-block:: ini 61 | 62 | [DEFAULT] 63 | ... 64 | client_id = hostname_of_machine 65 | jobs_dir = /etc/freezer/scheduler/conf.d 66 | #enable_v1_api = False 67 | 68 | 69 | 3. Start ``freezer-scheduler`` 70 | 71 | .. code-block:: console 72 | 73 | $ . admin-openrc 74 | $ sudo freezer-scheduler --config-file /etc/freezer/scheduler.conf start 75 | 76 | -------------------------------------------------------------------------------- /doc/source/install/next-steps.rst: -------------------------------------------------------------------------------- 1 | Next steps 2 | ~~~~~~~~~~ 3 | 4 | Your OpenStack environment now includes the freezer-api service. 5 | 6 | To add additional services, see 7 | https://docs.openstack.org/install/ 8 | -------------------------------------------------------------------------------- /doc/source/install/verify.rst: -------------------------------------------------------------------------------- 1 | Verify operation 2 | ~~~~~~~~~~~~~~~~ 3 | 4 | Verify operation of the Backup service. 5 | 6 | .. note:: 7 | 8 | Perform these commands on the controller node. 9 | 10 | #. Source the ``admin`` project credentials to gain access to 11 | admin-only CLI commands: 12 | 13 | .. code-block:: console 14 | 15 | $ . admin-openrc 16 | 17 | #. List service components to verify successful launch and registration 18 | of each process: 19 | 20 | .. code-block:: console 21 | 22 | $ openstack endpoint list 23 | 24 | #. List available backup jobs for current node: 25 | 26 | .. code-block:: console 27 | 28 | $ freezer job-list 29 | -------------------------------------------------------------------------------- /doc/source/reference/index.rst: -------------------------------------------------------------------------------- 1 | Reference 2 | ========= 3 | 4 | .. toctree:: 5 | :maxdepth: 2 6 | 7 | api/modules 8 | -------------------------------------------------------------------------------- /doc/source/user/freezer-scheduler.rst: -------------------------------------------------------------------------------- 1 | Scheduler User Guide 2 | ==================== 3 | 4 | Here goes the guide... 5 | -------------------------------------------------------------------------------- /doc/source/user/freezer-web-ui.rst: -------------------------------------------------------------------------------- 1 | Web User Interface User Guide 2 | ============================= 3 | 4 | Here goes the guide... 5 | -------------------------------------------------------------------------------- /doc/source/user/index.rst: -------------------------------------------------------------------------------- 1 | User Guide 2 | ========== 3 | 4 | .. toctree:: 5 | :maxdepth: 2 6 | 7 | installation 8 | freezer-agent 9 | freezer-scheduler 10 | freezer-web-ui 11 | -------------------------------------------------------------------------------- /etc/config-generator.conf: -------------------------------------------------------------------------------- 1 | [DEFAULT] 2 | output_file = etc/scheduler.conf.sample 3 | wrap_width = 79 4 | namespace = freezer-scheduler 5 | namespace = oslo.log 6 | 7 | -------------------------------------------------------------------------------- /freezer/__init__.py: -------------------------------------------------------------------------------- 1 | # (c) Copyright 2014,2015 Hewlett-Packard Development Company, L.P. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # Freezer Versions 16 | 17 | import pbr.version 18 | 19 | 20 | __version__ = pbr.version.VersionInfo('freezer').version_string() 21 | version_info = pbr.version.VersionInfo('freezer') 22 | -------------------------------------------------------------------------------- /freezer/common/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/freezer/f0e435038467d1fe284ec4b8e87f70bfaa41f490/freezer/common/__init__.py -------------------------------------------------------------------------------- /freezer/common/client_manager.py: -------------------------------------------------------------------------------- 1 | # (c) Copyright 2016 Hewlett-Packard Development Enterprise, L.P. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | 16 | from freezer.openstack import osclients 17 | from freezer.utils import config 18 | from oslo_config import cfg 19 | from oslo_log import log 20 | 21 | CONF = cfg.CONF 22 | LOG = log.getLogger(__name__) 23 | 24 | 25 | def parse_osrc(file_name): 26 | with open(file_name, 'r') as osrc_file: 27 | return config.osrc_parse(osrc_file.read()) 28 | 29 | 30 | def get_client_manager(backup_args): 31 | if "osrc" in backup_args: 32 | options = osclients.OpenstackOpts.create_from_dict( 33 | parse_osrc(backup_args['osrc'])) 34 | else: 35 | options = osclients.OpenstackOpts.create_from_env().get_opts_dicts() 36 | if backup_args['project_id']: 37 | options['project_name'] = None 38 | options['project_id'] = backup_args['project_id'] 39 | client_manager = osclients.OSClientManager( 40 | auth_url=options.pop('auth_url', None), 41 | auth_method=options.pop('auth_method', 'password'), 42 | dry_run=backup_args.get('dry_run', None), 43 | **options 44 | ) 45 | return client_manager 46 | -------------------------------------------------------------------------------- /freezer/engine/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/freezer/f0e435038467d1fe284ec4b8e87f70bfaa41f490/freezer/engine/__init__.py -------------------------------------------------------------------------------- /freezer/engine/glance/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/freezer/f0e435038467d1fe284ec4b8e87f70bfaa41f490/freezer/engine/glance/__init__.py -------------------------------------------------------------------------------- /freezer/engine/manager.py: -------------------------------------------------------------------------------- 1 | # (c) Copyright 2016 Hewlett-Packard Development Enterprise, L.P. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | 16 | import os 17 | from oslo_config import cfg 18 | from oslo_log import log 19 | from oslo_utils import importutils 20 | 21 | from freezer.exceptions import engine as engine_exceptions 22 | 23 | CONF = cfg.CONF 24 | LOG = log.getLogger(__name__) 25 | 26 | 27 | class EngineManager(object): 28 | """ 29 | EngineManager, this class handles engines. 30 | Lists all available engines 31 | Checks if an engine does exists or not 32 | Load an engine 33 | """ 34 | def __init__(self): 35 | """ 36 | This function does some initialization for the engine manager and 37 | register some variables from the CONF variable into the local class 38 | """ 39 | self.engine_name = CONF.engine_name 40 | self.engine_store = os.path.abspath(os.path.dirname(__file__)) 41 | self.engines = self.list_engines() 42 | 43 | def load_engine(self, **kwargs): 44 | """ Check if the engine exists or not. If not raise EngineNotFound 45 | Error. If the engine exists then try to get an instance of this engine. 46 | """ 47 | if not self._check_engine_exists(): 48 | raise engine_exceptions.EngineNotFound( 49 | "Engine {0} not found".format(self.engine_name) 50 | ) 51 | return importutils.import_object( 52 | "freezer.engine.{0}.{0}.{1}Engine".format( 53 | self.engine_name, 54 | self.engine_name.capitalize() 55 | ), 56 | **kwargs 57 | ) 58 | 59 | def _check_engine_exists(self): 60 | if self.engine_name not in self.engines: 61 | return False 62 | return True 63 | 64 | def list_engines(self): 65 | """ 66 | Lists all engines in the engine directory 67 | :return: 68 | """ 69 | engines = [ 70 | name for name in os.listdir(self.engine_store) if 71 | os.path.isdir(os.path.join(self.engine_store, name)) 72 | ] 73 | return engines 74 | -------------------------------------------------------------------------------- /freezer/engine/nova/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/freezer/f0e435038467d1fe284ec4b8e87f70bfaa41f490/freezer/engine/nova/__init__.py -------------------------------------------------------------------------------- /freezer/engine/osbrick/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/freezer/f0e435038467d1fe284ec4b8e87f70bfaa41f490/freezer/engine/osbrick/__init__.py -------------------------------------------------------------------------------- /freezer/engine/osbrick/brick_utils.py: -------------------------------------------------------------------------------- 1 | 2 | # Copyright 2011-2014 OpenStack Foundation 3 | # All Rights Reserved. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. You may obtain 7 | # a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations 15 | # under the License. 16 | 17 | import os 18 | import socket 19 | 20 | from cinderclient import exceptions 21 | from oslo_concurrency import processutils 22 | 23 | 24 | def get_my_ip(): 25 | try: 26 | csock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 27 | csock.connect(('8.8.8.8', 80)) 28 | (addr, port) = csock.getsockname() 29 | csock.close() 30 | return addr 31 | except socket.error: 32 | return None 33 | 34 | 35 | def get_root_helper(): 36 | # NOTE (e0ne): We don't use rootwrap now 37 | return 'sudo' 38 | 39 | 40 | def require_root(f): 41 | def wrapper(*args, **kwargs): 42 | if os.getuid() != 0: 43 | raise exceptions.CommandError( 44 | "This command requires root permissions.") 45 | return f(*args, **kwargs) 46 | return wrapper 47 | 48 | 49 | def safe_execute(cmd): 50 | try: 51 | processutils.execute(*cmd, root_helper=get_root_helper(), 52 | run_as_root=True) 53 | except processutils.ProcessExecutionError as e: 54 | print('Command "{0}" execution returned {1} exit code:'.format( 55 | e.cmd, e.exit_code)) 56 | print('Stderr: {0}'.format(e.stderr)) 57 | print('Stdout: {0}'.format(e.stdout)) 58 | -------------------------------------------------------------------------------- /freezer/engine/osbrick/volume_actions.py: -------------------------------------------------------------------------------- 1 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 2 | # not use this file except in compliance with the License. You may obtain 3 | # a copy of the License at 4 | # 5 | # http://www.apache.org/licenses/LICENSE-2.0 6 | # 7 | # Unless required by applicable law or agreed to in writing, software 8 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 9 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 10 | # License for the specific language governing permissions and limitations 11 | # under the License. 12 | 13 | from os_brick import exception 14 | from os_brick import initiator 15 | 16 | 17 | class VolumeAction(object): 18 | def __init__(self, volumes_client, volume_id): 19 | self.volumes_client = volumes_client 20 | self.volume_id = volume_id 21 | 22 | def __enter__(self): 23 | return self 24 | 25 | def __exit__(self, type, value, traceback): 26 | if traceback: 27 | self.volumes_client.volumes.unreserve(self.volume_id) 28 | return False 29 | return True 30 | 31 | 32 | class Reserve(VolumeAction): 33 | def reserve(self): 34 | self.volumes_client.volumes.reserve(self.volume_id) 35 | 36 | 37 | class InitializeConnection(VolumeAction): 38 | def initialize(self, brick_client, multipath, enforce_multipath): 39 | conn_prop = brick_client.get_connector(multipath, enforce_multipath) 40 | return self.volumes_client.volumes.initialize_connection( 41 | self.volume_id, conn_prop) 42 | 43 | 44 | class VerifyProtocol(VolumeAction): 45 | # NOTE(e0ne): Only iSCSI and RBD based drivers are supported. NFS doesn't 46 | # work. Drivers with other protocols are not tested yet. 47 | SUPPORTED_PROCOTOLS = [initiator.ISCSI, initiator.RBD] 48 | 49 | def verify(self, protocol): 50 | protocol = protocol.upper() 51 | 52 | # NOTE(e0ne): iSCSI drivers works without issues, RBD and NFS don't 53 | # work. Drivers with other protocols are not tested yet. 54 | if protocol not in VerifyProtocol.SUPPORTED_PROCOTOLS: 55 | raise exception.ProtocolNotSupported(protocol=protocol) 56 | 57 | 58 | class ConnectVolume(VolumeAction): 59 | def connect(self, brick_connector, connection_data, 60 | mountpoint, mode, hostname): 61 | device_info = brick_connector.connect_volume(connection_data) 62 | 63 | self.volumes_client.volumes.attach(self.volume_id, instance_uuid=None, 64 | mountpoint=mountpoint, 65 | mode=mode, 66 | host_name=hostname) 67 | return device_info 68 | 69 | 70 | class VolumeDetachAction(VolumeAction): 71 | def __exit__(self, type, value, traceback): 72 | if traceback: 73 | self.volumes_client.volumes.roll_detaching(self.volume_id) 74 | return False 75 | return True 76 | 77 | 78 | class BeginDetach(VolumeDetachAction): 79 | def reserve(self): 80 | self.volumes_client.volumes.begin_detaching(self.volume_id) 81 | 82 | 83 | class InitializeConnectionForDetach(InitializeConnection, VolumeDetachAction): 84 | pass 85 | 86 | 87 | class DisconnectVolume(VolumeDetachAction): 88 | def disconnect(self, brick_connector, connection_data, device_info): 89 | device_info = device_info or {} 90 | 91 | brick_connector.disconnect_volume(connection_data, device_info) 92 | 93 | 94 | class DetachVolume(VolumeDetachAction): 95 | def detach(self, brick_client, 96 | attachment_uuid, multipath, enforce_multipath): 97 | conn_prop = brick_client.get_connector(multipath, enforce_multipath) 98 | 99 | self.volumes_client.volumes.terminate_connection(self.volume_id, 100 | conn_prop) 101 | self.volumes_client.volumes.detach(self.volume_id, attachment_uuid) 102 | -------------------------------------------------------------------------------- /freezer/engine/rsync/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/freezer/f0e435038467d1fe284ec4b8e87f70bfaa41f490/freezer/engine/rsync/__init__.py -------------------------------------------------------------------------------- /freezer/engine/rsyncv2/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/freezer/f0e435038467d1fe284ec4b8e87f70bfaa41f490/freezer/engine/rsyncv2/__init__.py -------------------------------------------------------------------------------- /freezer/engine/rsyncv2/pyrsync.py: -------------------------------------------------------------------------------- 1 | # Licensed under the Apache License, Version 2.0 (the "License"); 2 | # you may not use this file except in compliance with the License. 3 | # You may obtain a copy of the License at 4 | # 5 | # http://www.apache.org/licenses/LICENSE-2.0 6 | # 7 | # Unless required by applicable law or agreed to in writing, software 8 | # distributed under the License is distributed on an "AS IS" BASIS, 9 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | # See the License for the specific language governing permissions and 11 | # limitations under the License. 12 | 13 | 14 | import hashlib 15 | import zlib 16 | 17 | 18 | _BASE = 65521 # largest prime smaller than 65536 19 | 20 | 21 | def adler32fast(data): 22 | return zlib.adler32(data) & 0xffffffff 23 | 24 | 25 | def adler32(data): 26 | checksum = zlib.adler32(data) 27 | s2, s1 = (checksum >> 16) & 0xffff, checksum & 0xffff 28 | return checksum & 0xffffffff, s1, s2 29 | 30 | 31 | def adler32rolling(removed, new, s1, s2, blocksize=4096): 32 | r = ord(removed) 33 | n = ord(new) 34 | s1 = (s1 + n - r) % _BASE 35 | s2 = (s2 + s1 - blocksize * r - 1) % _BASE 36 | return ((s2 << 16) | s1) & 0xffffffff, s1, s2 37 | 38 | 39 | def blockchecksums(args): 40 | """ 41 | Returns a list of weak and strong hashes for each block of the 42 | defined size for the given data stream. 43 | """ 44 | path, blocksize = args 45 | weakhashes = [] 46 | stronghashes = [] 47 | weak_append = weakhashes.append 48 | strong_append = stronghashes.append 49 | 50 | with open(path, 'rb') as instream: 51 | instream_read = instream.read 52 | read = instream_read(blocksize) 53 | 54 | while read: 55 | weak_append(adler32fast(read)) 56 | strong_append(hashlib.sha1(read).hexdigest()) 57 | read = instream_read(blocksize) 58 | 59 | return weakhashes, stronghashes 60 | 61 | 62 | def rsyncdelta_fast(datastream, remotesignatures, blocksize=4096): 63 | rem_weak, rem_strong = remotesignatures 64 | data_block = datastream.read(blocksize) 65 | index = 0 66 | while data_block: 67 | try: 68 | if adler32fast(data_block) == rem_weak[index] and hashlib.sha1( 69 | data_block).hexdigest() == rem_strong[index]: 70 | yield index 71 | else: 72 | yield data_block 73 | except IndexError: 74 | yield data_block 75 | 76 | index += 1 77 | data_block = datastream.read(blocksize) 78 | -------------------------------------------------------------------------------- /freezer/engine/tar/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/freezer/f0e435038467d1fe284ec4b8e87f70bfaa41f490/freezer/engine/tar/__init__.py -------------------------------------------------------------------------------- /freezer/exceptions/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/freezer/f0e435038467d1fe284ec4b8e87f70bfaa41f490/freezer/exceptions/__init__.py -------------------------------------------------------------------------------- /freezer/exceptions/engine.py: -------------------------------------------------------------------------------- 1 | # (c) Copyright 2016 Hewlett-Packard Development Enterprise, L.P. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | 16 | class EngineException(Exception): 17 | msg = "An unknown error occurred." 18 | 19 | def __init__(self, message=None, **kwargs): 20 | if not message: 21 | message = self.msg 22 | super(EngineException, self).__init__(message, kwargs) 23 | 24 | 25 | class EngineNotFound(EngineException): 26 | 27 | def __init__(self, message): 28 | super(EngineNotFound, self).__init__(message) 29 | -------------------------------------------------------------------------------- /freezer/exceptions/utils.py: -------------------------------------------------------------------------------- 1 | class TimeoutException(Exception): 2 | msg = "Timeout has been occurred." 3 | 4 | def __init__(self, message=None, **kwargs): 5 | if not message: 6 | message = self.msg 7 | super(TimeoutException, self).__init__(message, kwargs) 8 | -------------------------------------------------------------------------------- /freezer/lib/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/freezer/f0e435038467d1fe284ec4b8e87f70bfaa41f490/freezer/lib/__init__.py -------------------------------------------------------------------------------- /freezer/lib/pep3143daemon/__init__.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa 2 | # 3 | # Copyright (c) 2014, Stephan Schultchen. 4 | # 5 | # License: MIT (see LICENSE for details) 6 | 7 | 8 | """ 9 | pep3143daemon is a implementation of the PEP 3143, describing a well behaving 10 | Unix daemon, as documented in Stevens 'Unix Network Programming' 11 | """ 12 | 13 | from freezer.lib.pep3143daemon.daemon import DaemonContext, DaemonError 14 | from freezer.lib.pep3143daemon.pidfile import PidFile 15 | 16 | 17 | __all__ = [ 18 | "DaemonContext", 19 | "DaemonError", 20 | "PidFile", 21 | ] 22 | -------------------------------------------------------------------------------- /freezer/lib/pep3143daemon/pidfile.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa 2 | # The MIT License (MIT) 3 | # 4 | # Copyright (c) 2014 Stephan Schultchen 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in 14 | # all copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | # THE SOFTWARE. 23 | # -*- coding: utf-8 -*- 24 | """ 25 | Simple PidFile Module for a pep3143 daemon implementation. 26 | 27 | """ 28 | __author__ = 'schlitzer' 29 | 30 | 31 | import atexit 32 | import fcntl 33 | import os 34 | 35 | 36 | class PidFile(object): 37 | """ 38 | PidFile implementation for PEP 3143 Daemon. 39 | 40 | This Class can also be used with pythons 'with' 41 | statement. 42 | 43 | :param pidfile: 44 | filename to be used as pidfile, including path 45 | :type pidfile: str 46 | """ 47 | 48 | def __init__(self, pidfile): 49 | """ 50 | Create a new instance 51 | """ 52 | self._pidfile = pidfile 53 | self.pidfile = None 54 | 55 | def __enter__(self): 56 | self.acquire() 57 | return self 58 | 59 | def __exit__(self, exc_type, exc_value, exc_tb): 60 | if exc_type is not None: 61 | self.release() 62 | return False 63 | self.release() 64 | return True 65 | 66 | def acquire(self): 67 | """Acquire the pidfile. 68 | 69 | Create the pidfile, lock it, write the pid into it 70 | and register the release with atexit. 71 | 72 | 73 | :return: None 74 | :raise: SystemExit 75 | """ 76 | try: 77 | pidfile = open(self._pidfile, "a") 78 | except IOError as err: 79 | raise SystemExit(err) 80 | try: 81 | fcntl.flock(pidfile.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB) 82 | except IOError: 83 | raise SystemExit('Already running according to ' + self._pidfile) 84 | pidfile.seek(0) 85 | pidfile.truncate() 86 | pidfile.write(str(os.getpid()) + '\n') 87 | pidfile.flush() 88 | self.pidfile = pidfile 89 | atexit.register(self.release) 90 | 91 | def release(self): 92 | """Release the pidfile. 93 | 94 | Close and delete the Pidfile. 95 | 96 | 97 | :return: None 98 | """ 99 | try: 100 | self.pidfile.close() 101 | os.remove(self._pidfile) 102 | except OSError as err: 103 | if err.errno != 2: 104 | raise 105 | -------------------------------------------------------------------------------- /freezer/mode/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/freezer/f0e435038467d1fe284ec4b8e87f70bfaa41f490/freezer/mode/__init__.py -------------------------------------------------------------------------------- /freezer/mode/cinder.py: -------------------------------------------------------------------------------- 1 | # (c) Copyright 2016 Hewlett-Packard Enterprise Development , L.P. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from freezer.mode import mode 16 | 17 | 18 | class CinderMode(mode.Mode): 19 | """ 20 | Execute a cinder backup 21 | """ 22 | def __init__(self, conf): 23 | self.conf = conf 24 | 25 | @property 26 | def name(self): 27 | return "cinder" 28 | 29 | @property 30 | def version(self): 31 | return "1.0" 32 | 33 | def release(self): 34 | pass 35 | 36 | def prepare(self): 37 | pass 38 | -------------------------------------------------------------------------------- /freezer/mode/cindernative.py: -------------------------------------------------------------------------------- 1 | # (c) Copyright 2016 Hewlett-Packard Enterprise Development , L.P. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from freezer.mode import mode 16 | 17 | 18 | class CindernativeMode(mode.Mode): 19 | """ 20 | Execute a cinder-volume native backup/restore 21 | """ 22 | def __init__(self, conf): 23 | self.conf = conf 24 | 25 | @property 26 | def name(self): 27 | return "cindernative" 28 | 29 | @property 30 | def version(self): 31 | return "1.0" 32 | 33 | def release(self): 34 | pass 35 | 36 | def prepare(self): 37 | pass 38 | -------------------------------------------------------------------------------- /freezer/mode/fs.py: -------------------------------------------------------------------------------- 1 | # (c) Copyright 2015,2016 Hewlett-Packard Development Company, L.P. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from freezer.mode import mode 16 | 17 | 18 | class FsMode(mode.Mode): 19 | 20 | def __init__(self, conf): 21 | pass 22 | 23 | @property 24 | def name(self): 25 | return "fs" 26 | 27 | @property 28 | def version(self): 29 | return "1.0" 30 | 31 | def release(self): 32 | pass 33 | 34 | def prepare(self): 35 | pass 36 | -------------------------------------------------------------------------------- /freezer/mode/glance.py: -------------------------------------------------------------------------------- 1 | # (c) Copyright 2019 ZTE Corporation.. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from freezer.mode import mode 16 | 17 | 18 | class GlanceMode(mode.Mode): 19 | """ 20 | Execute a glance backup/restore 21 | """ 22 | def __init__(self, conf): 23 | self.conf = conf 24 | 25 | @property 26 | def name(self): 27 | return "glance" 28 | 29 | @property 30 | def version(self): 31 | return "1.0" 32 | 33 | def release(self): 34 | pass 35 | 36 | def prepare(self): 37 | pass 38 | -------------------------------------------------------------------------------- /freezer/mode/mode.py: -------------------------------------------------------------------------------- 1 | # (c) Copyright 2015,2016 Hewlett-Packard Development Company, L.P. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import abc 16 | 17 | 18 | class Mode(metaclass=abc.ABCMeta): 19 | @property 20 | @abc.abstractmethod 21 | def name(self): 22 | pass 23 | 24 | @property 25 | @abc.abstractmethod 26 | def version(self): 27 | pass 28 | 29 | @abc.abstractmethod 30 | def prepare(self): 31 | pass 32 | 33 | @abc.abstractmethod 34 | def release(self): 35 | pass 36 | -------------------------------------------------------------------------------- /freezer/mode/mongo.py: -------------------------------------------------------------------------------- 1 | # (c) Copyright 2015,2016 Hewlett-Packard Development Company, L.P. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from oslo_log import log 16 | 17 | from freezer.mode import mode 18 | 19 | LOG = log.getLogger(__name__) 20 | 21 | 22 | class MongoMode(mode.Mode): 23 | """ 24 | Execute the necessary tasks for file system backup mode 25 | """ 26 | 27 | @property 28 | def name(self): 29 | return "mongo" 30 | 31 | @property 32 | def version(self): 33 | return "1.0" 34 | 35 | def release(self): 36 | pass 37 | 38 | def prepare(self): 39 | pass 40 | 41 | def __init__(self, conf): 42 | try: 43 | import pymongo 44 | except ImportError: 45 | raise ImportError('please install pymongo module') 46 | 47 | LOG.info('MongoDB backup is being executed...') 48 | LOG.info('Checking is the localhost is Master/Primary...') 49 | # todo unhardcode this 50 | mongodb_port = '27017' 51 | local_hostname = conf.hostname 52 | db_host_port = '{0}:{1}'.format(local_hostname, mongodb_port) 53 | mongo_client = pymongo.MongoClient(db_host_port) 54 | master_dict = dict(mongo_client.admin.command("isMaster")) 55 | if master_dict['me'] != master_dict['primary']: 56 | raise Exception('localhost {0} is not Master/Primary,\ 57 | exiting...'.format(local_hostname)) 58 | -------------------------------------------------------------------------------- /freezer/mode/mysql.py: -------------------------------------------------------------------------------- 1 | # (c) Copyright 2015,2016 Hewlett-Packard Development Company, L.P. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from freezer.mode import mode 16 | from freezer.utils import config 17 | 18 | 19 | class MysqlMode(mode.Mode): 20 | """ 21 | Execute a MySQL DB backup. currently only backup with lvm snapshots 22 | are supported. This mean, just before the lvm snap vol is created, 23 | the db tables will be flushed and locked for read, then the lvm create 24 | command will be executed and after that, the table will be unlocked and 25 | the backup will be executed. It is important to have the available in 26 | backup_args.mysql_conf the file where the database host, name, user, 27 | password and port are set. 28 | """ 29 | 30 | @property 31 | def name(self): 32 | return "mysql" 33 | 34 | @property 35 | def version(self): 36 | return "1.0" 37 | 38 | def release(self): 39 | if not self.released: 40 | self.released = True 41 | self.cursor.execute('UNLOCK TABLES') 42 | self.mysql_db_inst.commit() 43 | self.cursor.close() 44 | self.mysql_db_inst.close() 45 | 46 | def prepare(self): 47 | self.released = False 48 | self.cursor = self.mysql_db_inst.cursor() 49 | self.cursor.execute('FLUSH TABLES WITH READ LOCK') 50 | self.mysql_db_inst.commit() 51 | 52 | def __init__(self, conf): 53 | try: 54 | import pymysql as MySQLdb 55 | except ImportError: 56 | raise ImportError('Please install PyMySQL module') 57 | 58 | with open(conf.mysql_conf, 'r') as mysql_file_fd: 59 | parsed_config = config.ini_parse(mysql_file_fd) 60 | # Initialize the DB object and connect to the db according to 61 | # the db mysql backup file config 62 | self.released = False 63 | try: 64 | self.mysql_db_inst = MySQLdb.connect( 65 | host=parsed_config.get("host", False), 66 | port=int(parsed_config.get("port", 3306)), 67 | user=parsed_config.get("user", False), 68 | passwd=parsed_config.get("password", False)) 69 | self.cursor = None 70 | except Exception as error: 71 | raise Exception('MySQL: {0}'.format(error)) 72 | -------------------------------------------------------------------------------- /freezer/mode/nova.py: -------------------------------------------------------------------------------- 1 | # (c) Copyright 2016 Hewlett-Packard Enterprise Development , L.P. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from freezer.mode import mode 16 | 17 | 18 | class NovaMode(mode.Mode): 19 | """ 20 | Execute a nova backup/restore 21 | """ 22 | def __init__(self, conf): 23 | self.conf = conf 24 | 25 | @property 26 | def name(self): 27 | return "nova" 28 | 29 | @property 30 | def version(self): 31 | return "1.0" 32 | 33 | def release(self): 34 | pass 35 | 36 | def prepare(self): 37 | pass 38 | -------------------------------------------------------------------------------- /freezer/mode/sqlserver.py: -------------------------------------------------------------------------------- 1 | # (c) Copyright 2015,2016 Hewlett-Packard Development Company, L.P. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | 16 | from oslo_log import log 17 | 18 | from freezer.mode import mode 19 | from freezer.utils import config 20 | from freezer.utils import utils 21 | from freezer.utils import winutils 22 | 23 | LOG = log.getLogger(__name__) 24 | 25 | 26 | class SqlserverMode(mode.Mode): 27 | """ 28 | Execute a SQL Server DB backup. Currently only backups with shadow 29 | copy are supported. This mean, as soon as the shadow copy is created 30 | the db writes will be blocked and a checkpoint will be created, as soon 31 | as the backup finish the db will be unlocked and the backup will be 32 | uploaded. A sql_server.conf_file is required for this operation. 33 | """ 34 | def __init__(self, conf): 35 | self.released = False 36 | with open(conf.sql_server_conf, 'r') as sql_conf_file_fd: 37 | self.sql_server_instance = \ 38 | config.ini_parse(sql_conf_file_fd)["instance"] 39 | 40 | @property 41 | def name(self): 42 | return "sqlserver" 43 | 44 | @property 45 | def version(self): 46 | return "1.0" 47 | 48 | def stop_sql_server(self): 49 | """ Stop a SQL Server instance to 50 | perform the backup of the db files """ 51 | 52 | LOG.info('Stopping SQL Server for backup') 53 | with winutils.DisableFileSystemRedirection(): 54 | cmd = 'net stop "SQL Server ({0})"'\ 55 | .format(self.sql_server_instance) 56 | (out, err) = utils.create_subprocess(cmd) 57 | if err != '': 58 | raise Exception('Error while stopping SQL Server,' 59 | ', error {0}'.format(err)) 60 | 61 | def start_sql_server(self): 62 | """ Start the SQL Server instance after the backup is completed """ 63 | 64 | with winutils.DisableFileSystemRedirection(): 65 | cmd = 'net start "SQL Server ({0})"'.format( 66 | self.sql_server_instance) 67 | (out, err) = utils.create_subprocess(cmd) 68 | if err != '': 69 | raise Exception('Error while starting SQL Server' 70 | ', error {0}'.format(err)) 71 | LOG.info('SQL Server back to normal') 72 | 73 | def prepare(self): 74 | self.stop_sql_server() 75 | self.released = False 76 | 77 | def release(self): 78 | if not self.released: 79 | self.released = True 80 | self.start_sql_server() 81 | -------------------------------------------------------------------------------- /freezer/openstack/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/freezer/f0e435038467d1fe284ec4b8e87f70bfaa41f490/freezer/openstack/__init__.py -------------------------------------------------------------------------------- /freezer/openstack/backup.py: -------------------------------------------------------------------------------- 1 | # (c) Copyright 2014,2015 Hewlett-Packard Development Company, L.P. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | 16 | """ 17 | Freezer Backup modes related functions 18 | """ 19 | 20 | from oslo_config import cfg 21 | from oslo_log import log 22 | 23 | from freezer.utils import utils 24 | 25 | CONF = cfg.CONF 26 | LOG = log.getLogger(__name__) 27 | 28 | 29 | class BackupOs(object): 30 | 31 | def __init__(self, client_manager, container, storage): 32 | """ 33 | 34 | :param client_manager: 35 | :param container: 36 | :param storage: 37 | :type storage: freezer.swift.SwiftStorage 38 | :return: 39 | """ 40 | self.client_manager = client_manager 41 | self.container = container 42 | self.storage = storage 43 | 44 | def backup_cinder_by_glance(self, volume_id): 45 | """ 46 | Implements cinder backup: 47 | 1) Gets a stream of the image from glance 48 | 2) Stores resulted image to the swift as multipart object 49 | 50 | :param volume_id: id of volume for backup 51 | """ 52 | client_manager = self.client_manager 53 | cinder = client_manager.get_cinder() 54 | 55 | volume = cinder.volumes.get(volume_id) 56 | LOG.debug("Creation temporary snapshot") 57 | snapshot = client_manager.provide_snapshot( 58 | volume, "backup_snapshot_for_volume_%s" % volume_id) 59 | LOG.debug("Creation temporary volume") 60 | copied_volume = client_manager.do_copy_volume(snapshot) 61 | LOG.debug("Creation temporary glance image") 62 | image = client_manager.make_glance_image(copied_volume.id, 63 | copied_volume) 64 | LOG.debug("Download temporary glance image {0}".format(image.id)) 65 | stream = client_manager.download_image(image) 66 | package = "{0}/{1}".format(volume_id, utils.DateTime.now().timestamp) 67 | LOG.debug("Saving image to {0}".format(self.storage.type)) 68 | if volume.name is None: 69 | name = volume_id 70 | else: 71 | name = volume.name 72 | headers = {'x-object-meta-length': str(len(stream)), 73 | 'volume_name': name, 74 | 'availability_zone': volume.availability_zone 75 | } 76 | attachments = volume._info['attachments'] 77 | if attachments: 78 | headers['server'] = attachments[0]['server_id'] 79 | self.storage.add_stream(stream, package, headers=headers) 80 | LOG.debug("Deleting temporary snapshot") 81 | client_manager.clean_snapshot(snapshot) 82 | LOG.debug("Deleting temporary volume") 83 | cinder.volumes.delete(copied_volume) 84 | LOG.debug("Deleting temporary image") 85 | client_manager.get_glance().images.delete(image.id) 86 | 87 | def backup_cinder(self, volume_id, name=None, description=None, 88 | incremental=False): 89 | client_manager = self.client_manager 90 | cinder = client_manager.get_cinder() 91 | container = "{0}/{1}/{2}".format(self.container, volume_id, 92 | utils.DateTime.now().timestamp) 93 | if incremental: 94 | search_opts = { 95 | 'volume_id': volume_id, 96 | 'status': 'available' 97 | } 98 | backups = cinder.backups.list(search_opts=search_opts) 99 | if len(backups) <= 0: 100 | cinder.backups.create(volume_id, container, name, description, 101 | incremental=False, force=True) 102 | else: 103 | cinder.backups.create(volume_id, container, name, description, 104 | incremental=incremental, force=True) 105 | else: 106 | cinder.backups.create(volume_id, container, name, description, 107 | incremental=incremental, force=True) 108 | -------------------------------------------------------------------------------- /freezer/scheduler/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/freezer/f0e435038467d1fe284ec4b8e87f70bfaa41f490/freezer/scheduler/__init__.py -------------------------------------------------------------------------------- /freezer/scheduler/utils.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015 Hewlett-Packard 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | 16 | import os 17 | import signal 18 | import socket 19 | 20 | from freezerclient import exceptions 21 | from oslo_config import cfg 22 | from oslo_log import log 23 | from oslo_serialization import jsonutils as json 24 | from oslo_utils import uuidutils 25 | import psutil 26 | 27 | 28 | CONF = cfg.CONF 29 | LOG = log.getLogger(__name__) 30 | 31 | CONFIG_FILE_EXT = '.conf' 32 | 33 | 34 | def do_register(client, args=None): 35 | if client: 36 | client_info = { 37 | "client_id": client.client_id, 38 | "hostname": socket.gethostname(), 39 | "supported_actions": CONF.capabilities.supported_actions, 40 | "supported_modes": CONF.capabilities.supported_modes, 41 | "supported_storages": CONF.capabilities.supported_storages, 42 | "supported_engines": CONF.capabilities.supported_engines, 43 | } 44 | try: 45 | client.clients.create(client_info) 46 | except exceptions.ApiClientException as e: 47 | if e.status_code == 409: 48 | print("Client already registered") 49 | return 73 # os.EX_CANTCREAT 50 | return 0 # os.EX_OK 51 | 52 | 53 | def find_config_files(path): 54 | expanded_path = os.path.expanduser(path) 55 | if os.path.isfile(expanded_path): 56 | return [expanded_path] 57 | file_list = [] 58 | if os.path.isdir(expanded_path): 59 | for fname in next(os.walk(expanded_path))[2]: 60 | if CONFIG_FILE_EXT.upper() == os.path.splitext(fname)[1].upper(): 61 | file_list.append('{0}/{1}'.format(expanded_path, fname)) 62 | return file_list 63 | raise Exception("unable to find job files at the provided path " 64 | "{0}".format(path)) 65 | 66 | 67 | def load_doc_from_json_file(fname, debug=False): 68 | with open(fname, 'rb') as fd: 69 | try: 70 | doc = json.load(fd) 71 | except Exception as e: 72 | raise Exception("Unable to load conf file. {0}".format(e)) 73 | if debug: 74 | print("File {0} loaded: ".format(fname)) 75 | return doc 76 | 77 | 78 | def save_doc_to_json_file(doc, fname, debug=False): 79 | with open(fname, 'w') as fd: 80 | json.dump(doc, fd, indent=4) 81 | if debug: 82 | print('Saved doc to file: {0}'.format(fname)) 83 | 84 | 85 | def get_jobs_from_disk(path): 86 | job_doc_list = [ 87 | load_doc_from_json_file(f) for f in find_config_files(path)] 88 | for job_doc in job_doc_list: 89 | if job_doc: 90 | job_doc['job_id'] = job_doc.get('job_id', uuidutils.generate_uuid( 91 | dashed=False)) 92 | return [x for x in job_doc_list if x] 93 | 94 | 95 | def save_jobs_to_disk(job_doc_list, path): 96 | for doc in job_doc_list: 97 | fname = os.path.normpath('{0}/job_{1}.conf'. 98 | format(path, doc['job_id'])) 99 | save_doc_to_json_file(doc, fname) 100 | 101 | 102 | def get_active_jobs_from_api(client): 103 | # might raise 104 | search = {"match_not": [{"status": "completed"}]} 105 | job_list, offset = [], 0 106 | while True: 107 | jobs = client.jobs.list(limit=10, offset=offset, search=search) 108 | job_list.extend(jobs) 109 | if len(jobs) < 10: 110 | break 111 | offset += len(jobs) 112 | return job_list 113 | 114 | 115 | def terminate_subprocess(pid, name): 116 | try: 117 | process = psutil.Process(pid) 118 | if process.name.startswith(name): 119 | os.kill(pid, signal.SIGTERM) 120 | else: 121 | LOG.warning('The name {} does not match the pid {}'.format( 122 | name, pid)) 123 | except Exception: 124 | LOG.debug('Process {} does not exists anymore'.format(pid)) 125 | -------------------------------------------------------------------------------- /freezer/scheduler/win_service.py: -------------------------------------------------------------------------------- 1 | # Copyright 2014 Hewlett-Packard 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import os 16 | import servicemanager 17 | import sys 18 | import win32event 19 | import win32service 20 | import win32serviceutil 21 | 22 | from freezer.utils import winutils 23 | 24 | 25 | class PySvc(win32serviceutil.ServiceFramework): 26 | _svc_name_ = "FreezerService" 27 | _svc_display_name_ = "Freezer Service" 28 | _svc_description_ = "Freezer Service" 29 | 30 | def __init__(self, args): 31 | win32serviceutil.ServiceFramework.__init__(self, args) 32 | # create an event to listen for stop requests on 33 | self.hWaitStop = win32event.CreateEvent(None, 0, 0, None) 34 | self.home = r'C:\.freezer' 35 | self.insecure = False 36 | 37 | def SvcDoRun(self): 38 | """Run the windows service and start the scheduler in the background 39 | """ 40 | rc = None 41 | 42 | self.main() 43 | 44 | # if the stop event hasn't been fired keep looping 45 | while rc != win32event.WAIT_OBJECT_0: 46 | # block for 5 seconds and listen for a stop event 47 | rc = win32event.WaitForSingleObject(self.hWaitStop, 5000) 48 | 49 | def SvcStop(self): 50 | """Stop the windows service and stop the scheduler instance 51 | """ 52 | # tell the SCM we're shutting down 53 | self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING) 54 | 55 | # fire the stop event 56 | servicemanager.LogInfoMsg("freezer-scheduler stopped") 57 | win32event.SetEvent(self.hWaitStop) 58 | 59 | def main(self): 60 | from freezer.scheduler.freezer_scheduler import FreezerScheduler 61 | from freezerclient.v1.client import Client 62 | 63 | servicemanager.LogMsg( 64 | servicemanager.EVENTLOG_INFORMATION_TYPE, 65 | servicemanager.PYS_SERVICE_STARTED, 66 | (self._svc_name_, '')) 67 | 68 | winutils.set_environment(self.home) 69 | 70 | if os.environ.get('SERVICE_INSECURE'): 71 | self.insecure = True 72 | 73 | # only support for keystone v3 74 | credentials = {} 75 | # if os.environ['OS_IDENTITY_API_VERSION'] == 3: 76 | credentials = { 77 | 'username': os.environ['OS_USERNAME'], 78 | 'password': os.environ['OS_PASSWORD'], 79 | 'auth_url': os.environ['OS_AUTH_URL'], 80 | 'endpoint': os.environ['OS_BACKUP_URL'], 81 | 'project_name': os.environ['OS_PROJECT_NAME'], 82 | 'user_domain_name': os.environ['OS_USER_DOMAIN_NAME'], 83 | 'project_domain_name': os.environ['OS_PROJECT_DOMAIN_NAME'], 84 | 'insecure': self.insecure 85 | } 86 | 87 | client = Client(**credentials) 88 | 89 | scheduler = FreezerScheduler( 90 | apiclient=client, interval=int(os.environ['SERVICE_INTERVAL']), 91 | job_path=os.environ['SERVICE_JOB_PATH'], 92 | concurrent_jobs=int(os.environ['SERVICE_CONCURRENT_JOBS'])) 93 | 94 | scheduler.start() 95 | 96 | 97 | if __name__ == '__main__': 98 | if len(sys.argv) == 1: 99 | servicemanager.Initialize() 100 | servicemanager.PrepareToHostSingle(PySvc) 101 | servicemanager.StartServiceCtrlDispatcher() 102 | else: 103 | win32serviceutil.HandleCommandLine(PySvc) 104 | -------------------------------------------------------------------------------- /freezer/scripts/vss.ps1: -------------------------------------------------------------------------------- 1 | # (c) Copyright 2014,2015 Hewlett-Packard Development Company, L.P. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | param([String]$volume="") 16 | 17 | $shadow = get-wmiobject win32_shadowcopy 18 | 19 | # get static method 20 | $class=[WMICLASS]"root\cimv2:win32_shadowcopy" 21 | 22 | # create a new shadow copy 23 | $s1 = $class.create($volume, "ClientAccessible") 24 | 25 | # get shadow ID 26 | $s2 = gwmi Win32_ShadowCopy | ? { $_.ID -eq $s1.ShadowID } 27 | 28 | $d = $s2.DeviceObject + "\" 29 | 30 | # create a symlink for the shadow path 31 | cmd /c mklink /d $volume\freezer_shadowcopy "$d" 32 | 33 | echo "shadow id:" $s2 -------------------------------------------------------------------------------- /freezer/snapshot/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/freezer/f0e435038467d1fe284ec4b8e87f70bfaa41f490/freezer/snapshot/__init__.py -------------------------------------------------------------------------------- /freezer/snapshot/snapshot.py: -------------------------------------------------------------------------------- 1 | # (c) Copyright 2014,2015 Hewlett-Packard Development Company, L.P. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | 16 | """ 17 | Freezer Backup modes related functions 18 | """ 19 | 20 | from freezer.snapshot import lvm 21 | from freezer.snapshot import vss 22 | from freezer.utils import winutils 23 | 24 | 25 | def snapshot_create(backup_opt_dict): 26 | """ 27 | Calls the code to take fs snapshots, depending on the platform 28 | 29 | :param backup_opt_dict: 30 | :return: boolean value, True if snapshot has been taken, false otherwise 31 | """ 32 | if not backup_opt_dict.snapshot: 33 | return False 34 | 35 | if winutils.is_windows(): 36 | if backup_opt_dict.snapshot: 37 | # Create a shadow copy. 38 | backup_opt_dict.shadow_path, backup_opt_dict.shadow = \ 39 | vss.vss_create_shadow_copy(backup_opt_dict.windows_volume) 40 | 41 | backup_opt_dict.path_to_backup = winutils.use_shadow( 42 | backup_opt_dict.path_to_backup, 43 | backup_opt_dict.windows_volume) 44 | return True 45 | return False 46 | else: 47 | return lvm.lvm_snap(backup_opt_dict) 48 | 49 | 50 | def snapshot_remove(backup_opt_dict, shadow, windows_volume): 51 | if winutils.is_windows(): 52 | # Delete the shadow copy after the backup 53 | vss.vss_delete_shadow_copy(shadow, windows_volume) 54 | else: 55 | # Unmount and remove lvm snapshot volume 56 | lvm.lvm_snap_remove(backup_opt_dict) 57 | -------------------------------------------------------------------------------- /freezer/snapshot/vss.py: -------------------------------------------------------------------------------- 1 | # (c) Copyright 2014,2015 Hewlett-Packard Development Company, L.P. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import os 16 | 17 | from oslo_log import log 18 | 19 | from freezer.utils import utils 20 | from freezer.utils import winutils 21 | 22 | LOG = log.getLogger(__name__) 23 | 24 | 25 | def vss_create_shadow_copy(windows_volume): 26 | """ 27 | Create a new shadow copy for the specified volume 28 | 29 | Windows registry path for vssadmin: 30 | HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\VSS\Settings 31 | 32 | MaxShadowCopies 33 | Windows is limited in how many shadow copies can create per volume. 34 | The default amount of shadow copies is 64, the minimum is 1 and the maxi- 35 | mum is 512, if you want to change the default value you need to add/edit 36 | the key MaxShadowCopies and set the amount of shadow copies per volume. 37 | 38 | MinDiffAreaFileSize 39 | The minimum size of the shadow copy storage area is a per-computer setting 40 | that can be specified by using the MinDiffAreaFileSize registry value. 41 | 42 | If the MinDiffAreaFileSize registry value is not set, the minimum size of 43 | the shadow copy storage area is 32 MB for volumes that are smaller than 44 | 500 MB and 320 MB for volumes that are larger than 500 MB. 45 | 46 | If you have not set a maximum size, there is no limit to the amount 47 | of space that can be used. 48 | 49 | If the MinDiffAreaFileSize registry value does not exist, the backup 50 | application can create it under the following registry key: 51 | 52 | HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\VolSnap 53 | 54 | 55 | Freezer create a shadow copy for each time the client runs it's been 56 | removed after the backup is complete. 57 | 58 | :param volume: The letter of the windows volume e.g. c:\\ 59 | :return: shadow_id: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX 60 | :return: shadow_path: shadow copy path 61 | """ 62 | shadow_path = None 63 | shadow_id = None 64 | 65 | vss_delete_symlink(windows_volume) 66 | 67 | with winutils.DisableFileSystemRedirection(): 68 | path = os.path.dirname(os.path.abspath(__file__)) 69 | script = '{0}\\scripts\\vss.ps1'.format(path) 70 | (out, err) = utils.create_subprocess( 71 | ['powershell.exe', '-executionpolicy', 'unrestricted', 72 | '-command', script, '-volume', windows_volume]) 73 | if err != '': 74 | raise Exception('Error creating a new shadow copy on {0}' 75 | ', error {1}' .format(windows_volume, err)) 76 | 77 | for line in out.split('\n'): 78 | if 'symbolic' in line: 79 | shadow_path = line.split('>>')[1].strip() 80 | if '__RELPATH' in line: 81 | shadow_id = line.split('=')[1].strip().lower() + '}' 82 | shadow_id = shadow_id[1:] 83 | 84 | LOG.info('Created shadow copy {0}'.format(shadow_id)) 85 | 86 | return shadow_path, shadow_id 87 | 88 | 89 | def vss_delete_shadow_copy(shadow_id, windows_volume): 90 | """ 91 | Delete a shadow copy from the volume with the given shadow_id 92 | :param shadow_id: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX 93 | :return: bool 94 | """ 95 | 96 | with winutils.DisableFileSystemRedirection(): 97 | cmd = ['vssadmin', 'delete', 'shadows', 98 | '/shadow={0}'.format(shadow_id), '/quiet'] 99 | (out, err) = utils.create_subprocess(cmd) 100 | if err != '': 101 | raise Exception('Error deleting shadow copy with id {0}' 102 | ', error {1}' .format(shadow_id, err)) 103 | 104 | vss_delete_symlink(windows_volume) 105 | 106 | LOG.info('Deleting shadow copy {0}'.format(shadow_id)) 107 | 108 | return True 109 | 110 | 111 | def vss_delete_symlink(windows_volume): 112 | """Delete shadow copy symlink on the file system""" 113 | path = os.path.join(windows_volume, 'freezer_shadowcopy') 114 | try: 115 | if os.path.exists(path): 116 | os.rmdir(path) 117 | except Exception: 118 | LOG.error('Failed to delete shadow copy symlink {0}'. 119 | format(os.path.join(windows_volume, 120 | 'freezer_shadowcopy'))) 121 | -------------------------------------------------------------------------------- /freezer/storage/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/freezer/f0e435038467d1fe284ec4b8e87f70bfaa41f490/freezer/storage/__init__.py -------------------------------------------------------------------------------- /freezer/storage/exceptions.py: -------------------------------------------------------------------------------- 1 | # (c) Copyright 2016 Hewlett-Packard Enterprise Development Company, L.P. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | 16 | class StorageException(Exception): 17 | 18 | def __init__(self, message): 19 | super(StorageException, self).__init__(message) 20 | -------------------------------------------------------------------------------- /freezer/storage/fslike.py: -------------------------------------------------------------------------------- 1 | # (c) Copyright 2014,2015 Hewlett-Packard Development Company, L.P. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import abc 16 | 17 | from oslo_serialization import jsonutils as json 18 | 19 | from freezer.storage import physical 20 | 21 | 22 | class FsLikeStorage(physical.PhysicalStorage, metaclass=abc.ABCMeta): 23 | _type = 'fslike' 24 | 25 | def __init__(self, storage_path, 26 | max_segment_size, skip_prepare=False): 27 | super(FsLikeStorage, self).__init__( 28 | storage_path=storage_path, 29 | max_segment_size=max_segment_size, 30 | skip_prepare=skip_prepare) 31 | 32 | def prepare(self): 33 | self.create_dirs(self.storage_path) 34 | 35 | def info(self): 36 | pass 37 | 38 | def write_backup(self, rich_queue, backup): 39 | """ 40 | Stores backup in storage 41 | :type rich_queue: freezer.utils.streaming.RichQueue 42 | :type backup: freezer.storage.base.Backup 43 | """ 44 | backup = backup.copy(storage=self) 45 | path = backup.data_path 46 | self.create_dirs(path.rsplit('/', 1)[0]) 47 | 48 | with self.open(path, mode='wb') as \ 49 | b_file: 50 | for message in rich_queue.get_messages(): 51 | b_file.write(message) 52 | 53 | def backup_blocks(self, backup): 54 | """ 55 | 56 | :param backup: 57 | :type backup: freezer.storage.base.Backup 58 | :return: 59 | """ 60 | with self.open(backup.data_path, 'rb') as backup_file: 61 | while True: 62 | chunk = backup_file.read(self.max_segment_size) 63 | if len(chunk): 64 | yield chunk 65 | else: 66 | break 67 | 68 | @abc.abstractmethod 69 | def open(self, filename, mode): 70 | """ 71 | :type filename: str 72 | :param filename: 73 | :type mode: str 74 | :param mode: 75 | :return: 76 | """ 77 | pass 78 | 79 | def add_stream(self, stream, package_name, headers=None): 80 | """ 81 | :param stream: data 82 | :param package_name: path 83 | :param headers: backup metadata information 84 | :return: 85 | """ 86 | split = package_name.rsplit('/', 1) 87 | # create backup_basedir 88 | backup_basedir = "{0}/{1}".format(self.storage_path, 89 | package_name) 90 | self.create_dirs(backup_basedir) 91 | # define backup_data_name 92 | backup_basepath = "{0}/{1}".format(backup_basedir, 93 | split[0]) 94 | backup_metadata = "%s/metadata" % backup_basedir 95 | # write backup to backup_basepath 96 | with self.open(backup_basepath, 'wb') as backup_file: 97 | for el in stream: 98 | backup_file.write(el) 99 | # write data matadata to backup_metadata 100 | with self.open(backup_metadata, 'wb') as backup_meta: 101 | backup_meta.write(json.dumps(headers)) 102 | -------------------------------------------------------------------------------- /freezer/storage/local.py: -------------------------------------------------------------------------------- 1 | # (c) Copyright 2014,2015 Hewlett-Packard Development Company, L.P. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | 16 | import io 17 | import os 18 | import shutil 19 | 20 | from freezer.storage import fslike 21 | from freezer.utils import utils 22 | 23 | 24 | class LocalStorage(fslike.FsLikeStorage): 25 | _type = 'local' 26 | 27 | def get_file(self, from_path, to_path): 28 | shutil.copyfile(from_path, to_path) 29 | 30 | def put_file(self, from_path, to_path): 31 | shutil.copyfile(from_path, to_path) 32 | 33 | def listdir(self, directory): 34 | try: 35 | return os.listdir(directory) 36 | except OSError: 37 | return list() 38 | 39 | def create_dirs(self, path): 40 | utils.create_dir_tree(path) 41 | 42 | def rmtree(self, path): 43 | shutil.rmtree(path) 44 | 45 | def open(self, filename, mode): 46 | return io.open(filename, mode) 47 | -------------------------------------------------------------------------------- /freezer/storage/physical.py: -------------------------------------------------------------------------------- 1 | # (c) Copyright 2016 Hewlett-Packard Enterprise Development Company, L.P. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | 16 | import abc 17 | import os 18 | 19 | 20 | from freezer.storage import base 21 | from freezer.utils import utils 22 | 23 | 24 | class PhysicalStorage(base.Storage, metaclass=abc.ABCMeta): 25 | """ 26 | Backup like Swift, SSH or Local. Something that represents real storage. 27 | For example MultipleStorage is not physical. 28 | """ 29 | 30 | def __init__(self, storage_path, max_segment_size, 31 | skip_prepare=False): 32 | self.storage_path = storage_path 33 | self.max_segment_size = max_segment_size 34 | super(PhysicalStorage, self).__init__(skip_prepare=skip_prepare) 35 | 36 | def metadata_path(self, engine, hostname_backup_name): 37 | return utils.path_join(self.storage_path, "metadata", engine.name, 38 | hostname_backup_name) 39 | 40 | def get_level_zero(self, 41 | engine, 42 | hostname_backup_name, 43 | recent_to_date=None): 44 | """ 45 | Gets backups by backup_name and hostname 46 | 47 | :type engine: freezer.engine.engine.BackupEngine 48 | :param engine: Search for backups made by specified engine 49 | :type hostname_backup_name: str 50 | :param hostname_backup_name: Search for backup with specified name 51 | :type recent_to_date: int 52 | :param recent_to_date: 53 | :rtype: list[freezer.storage.base.Backup] 54 | :return: dictionary of level zero timestamps with attached storage 55 | """ 56 | 57 | path = self.metadata_path( 58 | engine=engine, 59 | hostname_backup_name=hostname_backup_name) 60 | 61 | zeros = [base.Backup( 62 | storage=self, 63 | engine=engine, 64 | hostname_backup_name=hostname_backup_name, 65 | level_zero_timestamp=int(t), 66 | timestamp=int(t), 67 | level=0) for t in self.listdir(path)] 68 | if recent_to_date: 69 | zeros = [zero for zero in zeros 70 | if zero.timestamp <= recent_to_date] 71 | return zeros 72 | 73 | @abc.abstractmethod 74 | def backup_blocks(self, backup): 75 | """ 76 | :param backup: 77 | :type backup: freezer.storage.base.Backup 78 | :return: 79 | """ 80 | pass 81 | 82 | @abc.abstractmethod 83 | def listdir(self, path): 84 | """ 85 | :type path: str 86 | :param path: 87 | :rtype: collections.Iterable[str] 88 | """ 89 | pass 90 | 91 | def put_metadata(self, 92 | engine_metadata_path, 93 | freezer_metadata_path, 94 | backup): 95 | """ 96 | :param engine_metadata_path: 97 | :param freezer_metadata_path: 98 | :type backup: freezer.storage.base.Backup 99 | :param backup: 100 | :return: 101 | """ 102 | backup = backup.copy(self) 103 | self.put_file(engine_metadata_path, backup.engine_metadata_path) 104 | self.create_dirs(os.path.dirname(backup.metadata_path)) 105 | self.put_file(freezer_metadata_path, backup.metadata_path) 106 | 107 | @abc.abstractmethod 108 | def rmtree(self, path): 109 | pass 110 | -------------------------------------------------------------------------------- /freezer/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/freezer/f0e435038467d1fe284ec4b8e87f70bfaa41f490/freezer/tests/__init__.py -------------------------------------------------------------------------------- /freezer/tests/integration/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/freezer/f0e435038467d1fe284ec4b8e87f70bfaa41f490/freezer/tests/integration/__init__.py -------------------------------------------------------------------------------- /freezer/tests/integration/test_version.py: -------------------------------------------------------------------------------- 1 | # (C) Copyright 2016 Hewlett Packard Enterprise Development Company LP 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | # not use this file except in compliance with the License. You may obtain 5 | # a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | # License for the specific language governing permissions and limitations 13 | # under the License. 14 | 15 | import subprocess 16 | 17 | from tempest.lib import decorators 18 | 19 | from freezer import __version__ as freezer_version 20 | from freezer.tests.integration import common 21 | 22 | 23 | class TestFreezerVersion(common.TestFS): 24 | 25 | @decorators.attr(type="gate") 26 | def test_version(self): 27 | 28 | version = subprocess.check_output(['freezer-agent', '--version'], 29 | stderr=subprocess.STDOUT) 30 | self.assertEqual(freezer_version, version.strip()) 31 | -------------------------------------------------------------------------------- /freezer/tests/unit/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/freezer/f0e435038467d1fe284ec4b8e87f70bfaa41f490/freezer/tests/unit/__init__.py -------------------------------------------------------------------------------- /freezer/tests/unit/engines/__init__.py: -------------------------------------------------------------------------------- 1 | __author__ = 'reldan' 2 | -------------------------------------------------------------------------------- /freezer/tests/unit/engines/nova/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/freezer/f0e435038467d1fe284ec4b8e87f70bfaa41f490/freezer/tests/unit/engines/nova/__init__.py -------------------------------------------------------------------------------- /freezer/tests/unit/engines/rsync/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/freezer/f0e435038467d1fe284ec4b8e87f70bfaa41f490/freezer/tests/unit/engines/rsync/__init__.py -------------------------------------------------------------------------------- /freezer/tests/unit/engines/rsync/test_pyrsync.py: -------------------------------------------------------------------------------- 1 | # (C) Copyright 2016 Mirantis, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import unittest 16 | 17 | import io 18 | 19 | from freezer.engine.rsync import pyrsync 20 | 21 | 22 | class TestPyrsync(unittest.TestCase): 23 | def test_blockcheksum(self): 24 | instream = io.BytesIO(b'aae9dd83aa45f906' 25 | b'a4629f42e97eac99' 26 | b'b9882284dc7030ca' 27 | b'427ad365fedd2a55') 28 | weak, strong = pyrsync.blockchecksums(instream, 16) 29 | exp_weak = [736756931, 616825970, 577963056, 633341072] 30 | exp_strong = ['0f923c37c14f648de4065d4666c2429231a923bc', 31 | '9f043572d40922cc45545bd6ec8a650ca095ab84', 32 | '3a0c39d59a6f49975c2be24bc6b37d80a6680dce', 33 | '81487d7e87190cfbbf4f74acc40094c0a6f6ce8a'] 34 | self.assertEqual((weak, strong), (exp_weak, exp_strong)) 35 | 36 | def test_rsyncdelta(self): 37 | datastream = io.BytesIO(b'addc830058f917ae' 38 | b'a1be5ab4d899b570' 39 | b'85c9534c64d8d71c' 40 | b'1f32cde9c71e5b6d') 41 | 42 | old_weak = [675087508, 698025105, 579470394, 667092162] 43 | old_strong = ['e72251cb70a1b918ee43876896ebb4c8a7225f78', 44 | '3bf6d2483425e8925df06c01ee490e386a9a707a', 45 | '0ba97d95cc49b1ee2863b7dec3d49911502111c2', 46 | '8b92d9f3f6679e1c8ce2f20e2a6217fd7f351f8f'] 47 | 48 | changed_indexes = [] 49 | cur_index = 0 50 | for block_index in pyrsync.rsyncdelta(datastream, 51 | (old_weak, old_strong), 16): 52 | if not isinstance(block_index, int): 53 | changed_indexes.append(cur_index) 54 | cur_index += 1 55 | exp_changed_indexes = [0, 2] 56 | self.assertEqual(changed_indexes[:-1], exp_changed_indexes) 57 | -------------------------------------------------------------------------------- /freezer/tests/unit/engines/tar/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/freezer/f0e435038467d1fe284ec4b8e87f70bfaa41f490/freezer/tests/unit/engines/tar/__init__.py -------------------------------------------------------------------------------- /freezer/tests/unit/engines/tar/test_tar_builders.py: -------------------------------------------------------------------------------- 1 | # (c) Copyright 2014,2015 Hewlett-Packard Development Company, L.P. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import unittest 16 | 17 | from freezer.engine.tar import tar_builders 18 | from freezer.utils import utils 19 | 20 | 21 | class TestTarCommandBuilder(unittest.TestCase): 22 | def setUp(self): 23 | self.builder = tar_builders.TarCommandBuilder(".", "gzip", False, 24 | "gnutar") 25 | 26 | def test_build(self): 27 | self.assertEqual( 28 | self.builder.build(), 29 | "gnutar --create -z --warning=none --no-check-device " 30 | "--one-file-system --preserve-permissions " 31 | "--same-owner --seek --ignore-failed-read .") 32 | 33 | def test_build_listed(self): 34 | self.builder.set_listed_incremental("listed-file.tar") 35 | self.assertEqual( 36 | self.builder.build(), 37 | "gnutar --create -z --warning=none --no-check-device " 38 | "--one-file-system --preserve-permissions --same-owner --seek " 39 | "--ignore-failed-read --listed-incremental=listed-file.tar .") 40 | 41 | def test_build_every_arg(self): 42 | self.builder.set_listed_incremental("listed-file.tar") 43 | self.builder.set_encryption("encrypt_pass_file", "openssl") 44 | self.builder.set_dereference("hard") 45 | self.builder.set_exclude("excluded_files") 46 | self.assertEqual( 47 | self.builder.build(), 48 | "gnutar --create -z --warning=none --no-check-device " 49 | "--one-file-system --preserve-permissions --same-owner " 50 | "--seek --ignore-failed-read --hard-dereference " 51 | "--listed-incremental=listed-file.tar " 52 | "--exclude=\"excluded_files\" . | openssl enc -aes-256-cfb -pass " 53 | "file:encrypt_pass_file && exit ${PIPESTATUS[0]}") 54 | 55 | def test_build_every_arg_windows(self): 56 | self.builder = tar_builders.TarCommandBuilder(".", "gzip", True, 57 | "gnutar") 58 | self.builder.set_listed_incremental("listed-file.tar") 59 | self.builder.set_encryption("encrypt_pass_file", "openssl") 60 | self.builder.set_dereference("hard") 61 | self.builder.set_exclude("excluded_files") 62 | self.assertEqual( 63 | self.builder.build(), 64 | 'gnutar -c -z --incremental --unlink-first --ignore-zeros ' 65 | '--hard-dereference --listed-incremental=listed-file.tar ' 66 | '--exclude="excluded_files" . ' 67 | '| openssl enc -aes-256-cfb -pass file:encrypt_pass_file ' 68 | '&& exit ${PIPESTATUS[0]}') 69 | 70 | 71 | class TestTarCommandRestoreBuilder(unittest.TestCase): 72 | def setUp(self): 73 | self.builder = tar_builders.TarCommandRestoreBuilder( 74 | "restore_path", "gzip", False, "gnutar") 75 | 76 | def test(self): 77 | self.assertEqual( 78 | self.builder.build(), 79 | "gnutar -z --incremental --extract --ignore-zeros " 80 | "--warning=none --overwrite --directory restore_path") 81 | 82 | def test_dry_run(self): 83 | self.builder.set_dry_run() 84 | self.assertEqual( 85 | self.builder.build(), 86 | "gnutar -z --incremental --list --ignore-zeros --warning=none") 87 | 88 | def test_all_args(self): 89 | self.builder.set_encryption("encrypt_pass_file", "openssl") 90 | self.assertEqual( 91 | self.builder.build(), 92 | "openssl enc -d -aes-256-cfb -pass file:encrypt_pass_file | " 93 | "gnutar -z --incremental --extract --ignore-zeros" 94 | " --warning=none --overwrite --directory restore_path " 95 | "&& exit ${PIPESTATUS[0]}") 96 | 97 | def test_all_args_windows(self): 98 | self.builder = tar_builders.TarCommandRestoreBuilder( 99 | "restore_path", "gzip", True, "gnutar") 100 | self.builder.set_encryption("encrypt_pass_file", "openssl") 101 | self.assertEqual( 102 | self.builder.build(), 103 | 'openssl enc -d -aes-256-cfb -pass file:encrypt_pass_file ' 104 | '| gnutar -x -z --incremental --unlink-first --ignore-zeros ' 105 | '&& exit ${PIPESTATUS[0]}') 106 | 107 | def test_get_tar_flag_from_algo(self): 108 | assert tar_builders.get_tar_flag_from_algo('gzip') == '-z' 109 | assert tar_builders.get_tar_flag_from_algo('bzip2') == '-j' 110 | if not utils.is_bsd(): 111 | assert tar_builders.get_tar_flag_from_algo('xz') == '-J' 112 | -------------------------------------------------------------------------------- /freezer/tests/unit/openstack/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/freezer/f0e435038467d1fe284ec4b8e87f70bfaa41f490/freezer/tests/unit/openstack/__init__.py -------------------------------------------------------------------------------- /freezer/tests/unit/openstack/test_admin.py: -------------------------------------------------------------------------------- 1 | """Freezer admin.py related tests 2 | 3 | (c) Copyright 2018 ZTE Corporation. 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | """ 17 | 18 | from freezer.openstack import admin 19 | from freezer.tests import commons 20 | 21 | 22 | class TestAdmin(commons.FreezerBaseTestCase): 23 | def setUp(self): 24 | super(TestAdmin, self).setUp() 25 | self.backup_opt = commons.BackupOpt1() 26 | self.admin_os = admin.AdminOs(self.backup_opt.client_manager) 27 | self.client_manager = self.backup_opt.client_manager 28 | 29 | def test_del_cinderbackup_and_dependend_incremental(self): 30 | self.admin_os.del_cinderbackup_and_dependend_incremental(1) 31 | try: 32 | self.admin_os.del_cinderbackup_and_dependend_incremental(1023) 33 | except Exception as e: 34 | msg = "Delete backup 1023 failed, the status of backup is error." 35 | self.assertEqual(msg, str(e)) 36 | 37 | try: 38 | self.admin_os.del_cinderbackup_and_dependend_incremental(1024) 39 | except Exception as e: 40 | msg = "Delete backup 1024 failed due to timeout over 120s," \ 41 | " the status of backup is deleting." 42 | self.assertEqual(msg, str(e)) 43 | 44 | def test_del_off_limit_fullbackup_keep(self): 45 | self.admin_os.del_off_limit_fullbackup('2', 1) 46 | 47 | def test_del_off_limit_fullbackup_keep_two(self): 48 | self.admin_os.del_off_limit_fullbackup('2', 2) 49 | 50 | def test_remove_cinderbackup_older_than(self): 51 | self.admin_os.remove_cinderbackup_older_than(35, 1463896546.0) 52 | try: 53 | self.admin_os.remove_cinderbackup_older_than(1023, 1463896546.0) 54 | except Exception as e: 55 | msg = "Delete backup 1023 failed, the status of backup is error." 56 | self.assertEqual(msg, str(e)) 57 | 58 | try: 59 | self.admin_os.remove_cinderbackup_older_than(1024, 1463896546.0) 60 | except Exception as e: 61 | msg = "Delete backup 1024 failed due to timeout over 120s," \ 62 | " the status of backup is deleting." 63 | self.assertEqual(msg, str(e)) 64 | -------------------------------------------------------------------------------- /freezer/tests/unit/openstack/test_backup.py: -------------------------------------------------------------------------------- 1 | """Freezer backup.py related tests 2 | 3 | (c) Copyright 2018 ZTE Corporation. 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | """ 17 | 18 | from freezer.openstack import backup 19 | from freezer.tests import commons 20 | 21 | 22 | class TestBackup(commons.FreezerBaseTestCase): 23 | def setUp(self): 24 | super(TestBackup, self).setUp() 25 | self.backup_opt = commons.BackupOpt1() 26 | self.bakup_os = backup.BackupOs(self.backup_opt.client_manager, 27 | self.backup_opt.container, 28 | self.backup_opt.storage) 29 | 30 | def test_backup_cinder_by_glance(self): 31 | self.bakup_os.backup_cinder_by_glance(35) 32 | 33 | def test_backup_cinder_by_glance_none_name(self): 34 | self.bakup_os.backup_cinder_by_glance(10230) 35 | 36 | def test_backup_cinder_with_incremental(self): 37 | self.bakup_os.backup_cinder(35, incremental=True) 38 | 39 | def test_backup_cinder_without_incremental(self): 40 | self.bakup_os.backup_cinder(35, incremental=False) 41 | 42 | def test_backup_cinder_with_none(self): 43 | self.bakup_os.backup_cinder(10240, incremental=True) 44 | -------------------------------------------------------------------------------- /freezer/tests/unit/openstack/test_osclients.py: -------------------------------------------------------------------------------- 1 | # (c) Copyright 2014,2015 Hewlett-Packard Development Company, L.P. 2 | # (c) Copyright 2016 Hewlett-Packard Enterprise Development Company, L.P. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | import unittest 17 | from unittest import mock 18 | 19 | from freezer.openstack import osclients 20 | 21 | 22 | class TestOsClients(unittest.TestCase): 23 | def setUp(self): 24 | self.opts = osclients.OpenstackOpts( 25 | username="user", project_name="project", 26 | auth_url="url/v3", password="password", identity_api_version="3", 27 | insecure=False, cacert='cert', user_domain_name='Default', 28 | project_domain_name='Default').get_opts_dicts() 29 | self.client_manager = osclients.OSClientManager(**self.opts) 30 | 31 | def test_init(self): 32 | self.client_manager.get_cinder() 33 | 34 | def test_create_cinder(self): 35 | self.client_manager.create_cinder() 36 | 37 | def test_create_swift(self): 38 | self.client_manager.create_swift() 39 | 40 | def test_create_nova(self): 41 | self.client_manager.create_nova() 42 | 43 | def test_create_neutron(self): 44 | self.client_manager.create_neutron() 45 | 46 | def test_dry_run(self): 47 | osclients.DryRunSwiftclientConnectionWrapper(mock.Mock()) 48 | 49 | def test_get_cinder(self): 50 | self.client_manager.get_cinder() 51 | 52 | def test_get_swift(self): 53 | self.client_manager.get_swift() 54 | 55 | def test_get_glance(self): 56 | self.client_manager.get_glance() 57 | 58 | def test_get_nova(self): 59 | self.client_manager.get_nova() 60 | 61 | def test_get_neutron(self): 62 | self.client_manager.get_neutron() 63 | -------------------------------------------------------------------------------- /freezer/tests/unit/scheduler/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/freezer/f0e435038467d1fe284ec4b8e87f70bfaa41f490/freezer/tests/unit/scheduler/__init__.py -------------------------------------------------------------------------------- /freezer/tests/unit/scheduler/commons.py: -------------------------------------------------------------------------------- 1 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 2 | # not use this file except in compliance with the License. You may obtain 3 | # a copy of the License at 4 | # 5 | # http://www.apache.org/licenses/LICENSE-2.0 6 | # 7 | # Unless required by applicable law or agreed to in writing, software 8 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 9 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 10 | # License for the specific language governing permissions and limitations 11 | # under the License. 12 | 13 | from freezer.scheduler import arguments 14 | from oslo_config import cfg 15 | 16 | CONF = cfg.CONF 17 | 18 | 19 | def set_test_capabilities(): 20 | arguments.configure_capabilities_options() 21 | CONF.capabilities.supported_actions = ['backup'] 22 | CONF.capabilities.supported_modes = ['cindernative'] 23 | CONF.capabilities.supported_storages = ['swift'] 24 | CONF.capabilities.supported_engines = [] 25 | 26 | 27 | def set_default_capabilities(): 28 | CONF.capabilities.supported_actions = arguments.DEFAULT_SUPPORTED_ACTIONS 29 | CONF.capabilities.supported_modes = arguments.DEFAULT_SUPPORTED_MODES 30 | CONF.capabilities.supported_storages = arguments.DEFAULT_SUPPORTED_STORAGES 31 | CONF.capabilities.supported_engines = arguments.DEFAULT_SUPPORTED_ENGINES 32 | -------------------------------------------------------------------------------- /freezer/tests/unit/scheduler/test_freezer_scheduler.py: -------------------------------------------------------------------------------- 1 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 2 | # not use this file except in compliance with the License. You may obtain 3 | # a copy of the License at 4 | # 5 | # http://www.apache.org/licenses/LICENSE-2.0 6 | # 7 | # Unless required by applicable law or agreed to in writing, software 8 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 9 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 10 | # License for the specific language governing permissions and limitations 11 | # under the License. 12 | 13 | import unittest 14 | from unittest import mock 15 | 16 | from freezer.scheduler.freezer_scheduler import FreezerScheduler 17 | from freezer.tests.unit.scheduler.commons import set_default_capabilities 18 | from freezer.tests.unit.scheduler.commons import set_test_capabilities 19 | 20 | SUPPORTED_JOB = { 21 | 'job_id': 'test2', 22 | 'job_schedule': {}, 23 | 'job_actions': [ 24 | {'freezer_action': {'action': 'backup'}}, 25 | ], 26 | } 27 | UNSUPPORTED_JOB = { 28 | 'job_id': 'test1', 29 | 'job_schedule': {}, 30 | 'job_actions': [ 31 | {'freezer_action': {'action': 'exec'}}, 32 | ], 33 | } 34 | 35 | 36 | class TestFreezerScheduler(unittest.TestCase): 37 | def setUp(self): 38 | self.scheduler = FreezerScheduler( 39 | apiclient=mock.MagicMock(), 40 | interval=1, 41 | job_path='/tmp/test', 42 | ) 43 | set_test_capabilities() 44 | 45 | def tearDown(self): 46 | set_default_capabilities() 47 | 48 | def test_filter_jobs(self): 49 | job_doc_list = [ 50 | SUPPORTED_JOB, 51 | UNSUPPORTED_JOB, 52 | ] 53 | expected_jobs = [SUPPORTED_JOB] 54 | filtered_jobs = self.scheduler.filter_jobs(job_doc_list) 55 | self.assertListEqual(filtered_jobs, expected_jobs) 56 | -------------------------------------------------------------------------------- /freezer/tests/unit/scheduler/test_utils.py: -------------------------------------------------------------------------------- 1 | # (c) Copyright 2019 ZTE Corporation. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import os 16 | import shutil 17 | import tempfile 18 | import unittest 19 | from unittest import mock 20 | from unittest.mock import patch 21 | 22 | from freezer.scheduler import utils 23 | 24 | job_list = [{"job_id": "test"}] 25 | 26 | 27 | class TestUtils(unittest.TestCase): 28 | def setUp(self): 29 | self.client = mock.Mock() 30 | self.client.clients.create = mock.Mock(return_value='test') 31 | self.client.jobs.list = mock.Mock(return_value=job_list) 32 | self.client.client_id = "test" 33 | 34 | def test_do_register(self): 35 | ret = utils.do_register(self.client, args=None) 36 | self.assertEqual(0, ret) 37 | 38 | def test_del_register_error(self): 39 | self.client.clients.delete = mock.Mock(side_effect=Exception( 40 | 'delete client error: bad request')) 41 | with self.assertRaises(Exception) as cm: # noqa 42 | utils.del_register(self.client) 43 | the_exception = cm.exception 44 | self.assertIn('delete client error', str(the_exception)) 45 | 46 | def test_find_config_files(self): 47 | temp = tempfile.NamedTemporaryFile('wb', delete=True, 48 | suffix='.conf') 49 | ret = utils.find_config_files(temp.name) 50 | self.assertEqual([temp.name], ret) 51 | temp.close() 52 | self.assertFalse(os.path.exists(temp.name)) 53 | 54 | def test_find_config_files_path(self): 55 | temp = tempfile.NamedTemporaryFile('wb', delete=True, 56 | suffix='.conf') 57 | temp_path = os.path.dirname(temp.name) 58 | ret = utils.find_config_files(temp_path) 59 | self.assertEqual([temp.name], ret) 60 | temp.close() 61 | self.assertFalse(os.path.exists(temp.name)) 62 | 63 | @patch('oslo_serialization.jsonutils.load') 64 | def test_load_doc_from_json_file(self, mock_load): 65 | os.mknod("/tmp/test_freezer.conf") 66 | mock_load.side_effect = Exception('error') 67 | try: 68 | utils.load_doc_from_json_file("/tmp/test_freezer.conf") 69 | except Exception as e: 70 | self.assertIn("Unable to load conf file", str(e)) 71 | os.remove("/tmp/test_freezer.conf") 72 | 73 | def test_get_jobs_from_disk(self): 74 | temp = tempfile.mkdtemp() 75 | file = '/'.join([temp, "test.conf"]) 76 | data = b'{"job_id": "test"}' 77 | with open(file, 'wb') as f: 78 | f.write(data) 79 | ret = utils.get_jobs_from_disk(temp) 80 | self.assertEqual(job_list, ret) 81 | shutil.rmtree(temp) 82 | self.assertFalse(os.path.exists(file)) 83 | 84 | def test_save_jobs_to_disk(self): 85 | job_doc_list = job_list 86 | tmpdir = tempfile.mkdtemp() 87 | utils.save_jobs_to_disk(job_doc_list, tmpdir) 88 | file = '/'.join([tmpdir, "job_test.conf"]) 89 | self.assertTrue(os.path.exists(file)) 90 | shutil.rmtree(tmpdir) 91 | 92 | def test_get_active_jobs_from_api(self): 93 | ret = utils.get_active_jobs_from_api(self.client) 94 | self.assertEqual(job_list, ret) 95 | 96 | @patch('os.kill') 97 | @patch('psutil.Process') 98 | def test_terminate_subprocess1(self, mock_process, mock_oskill): 99 | mock_pro = mock.MagicMock() 100 | mock_pro.name.startswith.return_value = False 101 | mock_process.return_value = mock_pro 102 | result = utils.terminate_subprocess(35, 'test') 103 | self.assertIsNone(result) 104 | mock_pro.name.startswith.return_value = True 105 | mock_oskill.side_effect = Exception("error") 106 | result = utils.terminate_subprocess(35, 'test') 107 | self.assertIsNone(result) 108 | 109 | @patch('psutil.Process') 110 | def test_terminate_subprocess(self, mock_process_constructor): 111 | mock_pro = mock_process_constructor.return_value 112 | seffect = mock.Mock( 113 | side_effect=Exception('Process 35 does not exists anymore')) 114 | mock_pro.raiseError.side_effect = seffect 115 | with self.assertRaises(Exception) as cm: # noqa 116 | utils.terminate_subprocess(35, "test") 117 | the_exception = cm.exception 118 | self.assertIn('does not exists anymore', 119 | str(the_exception)) 120 | -------------------------------------------------------------------------------- /freezer/tests/unit/snapshot/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/freezer/f0e435038467d1fe284ec4b8e87f70bfaa41f490/freezer/tests/unit/snapshot/__init__.py -------------------------------------------------------------------------------- /freezer/tests/unit/snapshot/test_vss.py: -------------------------------------------------------------------------------- 1 | # (c) Copyright 2014,2015 Hewlett-Packard Development Company, L.P. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import unittest 16 | 17 | from unittest import mock 18 | 19 | from freezer.tests.commons import FakeDisableFileSystemRedirection 20 | 21 | 22 | class TestVss(unittest.TestCase): 23 | def mock_process(self, process): 24 | fakesubprocesspopen = process.Popen() 25 | mock.patch('subprocess.Popen.communicate', 26 | new_callable=fakesubprocesspopen.communicate).start() 27 | mock.patch('subprocess.Popen', 28 | new_callable=fakesubprocesspopen.start()) 29 | 30 | def mock_winutils(self): 31 | fake_disable_redirection = FakeDisableFileSystemRedirection() 32 | mock.patch('winutils.DisableFileSystemRedirection.__enter__', 33 | new_callable=fake_disable_redirection.__enter__, 34 | ) 35 | mock.patch('winutils.DisableFileSystemRedirection.__exit__', 36 | new_callable=fake_disable_redirection.__exit__, 37 | ) 38 | 39 | # def test_vss_create_shadow_copy(self): 40 | # self.mock_process(FakeSubProcess()) 41 | # self.mock_winutils() 42 | # assert vss.vss_create_shadow_copy('C:\\') is not False 43 | # self.mock_process(FakeSubProcess3()) 44 | # self.assertRaises(Exception, vss.vss_create_shadow_copy('C:\\')) 45 | # 46 | # def test_vss_delete_shadow_copy(self): 47 | # self.mock_winutils() 48 | # self.mock_process(FakeSubProcess6()) 49 | # self.assertRaises(Exception, vss.vss_delete_shadow_copy('', '')) 50 | # self.mock_process(FakeSubProcess3()) 51 | # self.assertRaises(Exception, vss.vss_delete_shadow_copy('shadow_id', 52 | # 'C:\\')) 53 | # self.mock_process(FakeSubProcess()) 54 | # assert vss.vss_delete_shadow_copy('shadow_id', 'C:\\') is True 55 | -------------------------------------------------------------------------------- /freezer/tests/unit/storages/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/freezer/f0e435038467d1fe284ec4b8e87f70bfaa41f490/freezer/tests/unit/storages/__init__.py -------------------------------------------------------------------------------- /freezer/tests/unit/storages/test_local.py: -------------------------------------------------------------------------------- 1 | # (c) Copyright 2014,2015 Hewlett-Packard Development Company, L.P. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import shutil 16 | import tempfile 17 | import unittest 18 | 19 | from freezer.storage import local 20 | from freezer.utils import utils 21 | 22 | 23 | class TestLocalStorage(unittest.TestCase): 24 | BACKUP_DIR_PREFIX = "freezer_test_backup_dir" 25 | FILES_DIR_PREFIX = "freezer_test_files_dir" 26 | WORK_DIR_PREFIX = "freezer_work_dir" 27 | HELLO = "Hello World!\n" 28 | temp = True 29 | 30 | def create_content(self, files_dir, file_name="file_1", text=HELLO): 31 | f = open(files_dir + "/" + file_name, 'w') 32 | f.write(text) 33 | f.close() 34 | 35 | def create_dirs(self): 36 | tmpdir = tempfile.mkdtemp() 37 | if self.temp: 38 | backup_dir = tempfile.mkdtemp( 39 | dir=tmpdir, prefix=self.BACKUP_DIR_PREFIX) 40 | files_dir = tempfile.mkdtemp( 41 | dir=tmpdir, prefix=self.FILES_DIR_PREFIX) 42 | work_dir = tempfile.mkdtemp( 43 | dir=tmpdir, prefix=self.WORK_DIR_PREFIX) 44 | else: 45 | backup_dir = tmpdir + self.BACKUP_DIR_PREFIX 46 | files_dir = tmpdir + self.FILES_DIR_PREFIX 47 | work_dir = tmpdir + self.WORK_DIR_PREFIX 48 | utils.create_dir(backup_dir) 49 | utils.create_dir(work_dir) 50 | utils.create_dir(files_dir) 51 | self.create_content(files_dir) 52 | return backup_dir, files_dir, work_dir 53 | 54 | def remove_dirs(self, work_dir, files_dir, backup_dir): 55 | if self.temp: 56 | shutil.rmtree(work_dir) 57 | shutil.rmtree(files_dir) 58 | shutil.rmtree(backup_dir, ignore_errors=True) 59 | 60 | def remove_storage(self, backup_dir): 61 | shutil.rmtree(backup_dir) 62 | 63 | def test_prepare(self): 64 | backup_dir, files_dir, work_dir = self.create_dirs() 65 | storage = local.LocalStorage(backup_dir, 66 | work_dir, 67 | 10000) 68 | storage.prepare() 69 | 70 | def test_info(self): 71 | backup_dir, files_dir, work_dir = self.create_dirs() 72 | storage = local.LocalStorage(backup_dir, work_dir, 10000) 73 | storage.info() 74 | -------------------------------------------------------------------------------- /freezer/tests/unit/test_job.py: -------------------------------------------------------------------------------- 1 | # (c) Copyright 2014,2015 Hewlett-Packard Development Company, L.P. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | 16 | """ 17 | Freezer main.py related tests 18 | """ 19 | 20 | from unittest import mock 21 | from unittest.mock import patch 22 | 23 | from freezer import job as jobs 24 | from freezer.tests import commons 25 | 26 | 27 | class TestJob(commons.FreezerBaseTestCase): 28 | def setUp(self): 29 | super(TestJob, self).setUp() 30 | 31 | def test_execute(self): 32 | opt = commons.BackupOpt1() 33 | job = jobs.InfoJob(opt, opt.storage) 34 | assert job.execute() is not None 35 | 36 | 37 | class TestInfoJob(TestJob): 38 | 39 | def test_execute_nothing_to_do(self): 40 | backup_opt = commons.BackupOpt1() 41 | job = jobs.InfoJob(backup_opt, backup_opt.storage) 42 | job.execute() 43 | 44 | def test_execute_list_containers(self): 45 | backup_opt = commons.BackupOpt1() 46 | job = jobs.InfoJob(backup_opt, backup_opt.storage) 47 | job.execute() 48 | 49 | 50 | class TestBackupJob(TestJob): 51 | def setUp(self): 52 | super(TestBackupJob, self).setUp() 53 | 54 | def test_execute_backup_fs_no_incremental_and_backup_level_raise(self): 55 | backup_opt = commons.BackupOpt1() 56 | backup_opt.mode = 'default' 57 | backup_opt.no_incremental = True 58 | backup_opt.max_level = None 59 | backup_opt.always_level = None 60 | job = jobs.BackupJob(backup_opt, backup_opt.storage) 61 | self.assertRaises(Exception, job.execute) # noqa 62 | 63 | 64 | class TestAdminJob(TestJob): 65 | def setUp(self): 66 | super(TestAdminJob, self).setUp() 67 | 68 | def test_execute(self): 69 | backup_opt = commons.BackupOpt1() 70 | jobs.AdminJob(backup_opt, backup_opt.storage).execute() 71 | 72 | 73 | class TestExecJob(TestJob): 74 | def setUp(self): 75 | super(TestExecJob, self).setUp() 76 | # init mock_popen 77 | self.popen = patch('freezer.utils.exec_cmd.subprocess.Popen') 78 | self.mock_popen = self.popen.start() 79 | self.mock_popen.return_value = mock.Mock() 80 | self.mock_popen.return_value.communicate = mock.Mock() 81 | self.mock_popen.return_value.communicate.return_value = ['some stderr'] 82 | 83 | def tearDown(self): 84 | super(TestExecJob, self).tearDown() 85 | self.popen.stop() 86 | 87 | def test_execute_nothing_to_do(self): 88 | self.mock_popen.return_value.returncode = 0 89 | backup_opt = commons.BackupOpt1() 90 | backup_opt.command = 'ls ' 91 | jobs.ExecJob(backup_opt, backup_opt.storage).execute() 92 | 93 | def test_execute_script(self): 94 | self.mock_popen.return_value.returncode = 0 95 | backup_opt = commons.BackupOpt1() 96 | backup_opt.command = 'echo test' 97 | jobs.ExecJob(backup_opt, backup_opt.storage).execute() 98 | 99 | def test_execute_raise(self): 100 | self.popen = patch('freezer.utils.exec_cmd.subprocess.Popen') 101 | self.mock_popen = self.popen.start() 102 | self.mock_popen.return_value.returncode = 1 103 | backup_opt = commons.BackupOpt1() 104 | backup_opt.command = 'echo test' 105 | job = jobs.ExecJob(backup_opt, backup_opt.storage) 106 | self.assertRaises(Exception, job.execute) # noqa 107 | -------------------------------------------------------------------------------- /freezer/tests/unit/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/freezer/f0e435038467d1fe284ec4b8e87f70bfaa41f490/freezer/tests/unit/utils/__init__.py -------------------------------------------------------------------------------- /freezer/tests/unit/utils/test_config.py: -------------------------------------------------------------------------------- 1 | # (c) Copyright 2014,2015 Hewlett-Packard Development Company, L.P. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import io 16 | import unittest 17 | 18 | from freezer.utils import config 19 | 20 | 21 | class TestConfig(unittest.TestCase): 22 | def test_export(self): 23 | string = """unset OS_DOMAIN_NAME 24 | export OS_AUTH_URL="http://abracadabra/v3" 25 | export OS_PROJECT_NAME=abracadabra_project 26 | export OS_USERNAME=abracadabra_username 27 | export OS_PASSWORD=abracadabra_password 28 | export OS_PROJECT_DOMAIN_NAME=Default 29 | export OS_USER_DOMAIN_NAME=Default 30 | export OS_IDENTITY_API_VERSION=3 31 | export OS_AUTH_VERSION=3 32 | export OS_CACERT=/etc/ssl/certs/ca-certificates.crt 33 | export OS_ENDPOINT_TYPE=internalURL""" 34 | 35 | res = config.osrc_parse(string) 36 | self.assertEqual("http://abracadabra/v3", res["OS_AUTH_URL"]) 37 | 38 | def test_ini(self): 39 | string = """[default] 40 | # This is a comment line 41 | # 42 | host = 127.0.0.1 43 | port = 3306 44 | user = openstack 45 | password = 'aNiceQuotedPassword' 46 | password2 = "aNiceQuotedPassword" 47 | spaced = value""" 48 | 49 | fd = io.StringIO(string) 50 | res = config.ini_parse(fd) 51 | self.assertEqual('127.0.0.1', res['host']) 52 | self.assertEqual('openstack', res['user']) 53 | self.assertEqual('3306', res['port']) 54 | 55 | # python 3.4 tests will fail because aNiceQuatedPassword will 56 | # be quoted like "'aNiceQuotedPassword'" and '"aNiceQuotedPassword"'. 57 | # Solution for now is to strip the inside quotation marks. 58 | self.assertEqual('aNiceQuotedPassword', res['password'].strip("\"") 59 | .strip('\'')) 60 | self.assertEqual('aNiceQuotedPassword', res['password2'].strip("\"") 61 | .strip('\'')) 62 | 63 | self.assertEqual('value', res['spaced']) 64 | -------------------------------------------------------------------------------- /freezer/tests/unit/utils/test_crypt.py: -------------------------------------------------------------------------------- 1 | # (c) Copyright 2018 ZTE Corporation. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import shutil 16 | import tempfile 17 | import unittest 18 | 19 | # from unittest import mock 20 | # from mock import patch 21 | 22 | from freezer.utils import crypt 23 | 24 | 25 | class AESCipherTestCase(unittest.TestCase): 26 | 27 | def setUp(self): 28 | super(AESCipherTestCase, self).setUp() 29 | self.passwd_test_file_dir = None 30 | self.passwd_test_file_name = None 31 | self.create_pass_test_file() 32 | 33 | def tearDown(self): 34 | super(AESCipherTestCase, self).tearDown() 35 | self.delete_pass_test_file() 36 | 37 | def create_pass_test_file(self): 38 | if self.passwd_test_file_name: 39 | return 40 | tmpdir = tempfile.mkdtemp() 41 | FILES_DIR_PREFIX = "freezer_passwd_dir" 42 | files_dir = tempfile.mkdtemp(dir=tmpdir, prefix=FILES_DIR_PREFIX) 43 | file_name = "passwd_test" 44 | self.passwd_test_file_dir = files_dir 45 | text = '78f40f2c57eee727a4be179049cecf89' 46 | filehandle = open(files_dir + "/" + file_name, 'w') 47 | if filehandle: 48 | filehandle.write(text) 49 | filehandle.close() 50 | self.passwd_test_file_name = files_dir + "/" + file_name 51 | 52 | def delete_pass_test_file(self): 53 | if self.passwd_test_file_name: 54 | files_dir = self.passwd_test_file_dir 55 | shutil.rmtree(files_dir) 56 | self.passwd_test_file_name = None 57 | self.passwd_test_file_dir = None 58 | 59 | def test_get_pass_from_file(self): 60 | pfile = self.passwd_test_file_name 61 | passwd = crypt.AESCipher._get_pass_from_file(pfile) 62 | self.assertEqual(passwd, '78f40f2c57eee727a4be179049cecf89') 63 | 64 | # @unittest.skipIf(sys.version_info.major == 3, 65 | # 'Not supported on python v 3.x') 66 | def test_derive_key_and_iv(self): 67 | passwd = b'ababab' 68 | salt = b'a' 69 | ret1, ret2 = crypt.AESCipher._derive_key_and_iv(password=passwd, 70 | salt=salt, 71 | key_length=10, 72 | iv_length=5) 73 | expect1 = b'\xb3J5\xce\xd4b\x87\xce\xe0:' 74 | expect2 = b'\x93\xc9\x9d\x03\x00' 75 | self.assertEqual(ret1, expect1) 76 | self.assertEqual(ret2, expect2) 77 | -------------------------------------------------------------------------------- /freezer/tests/unit/utils/test_exec_cmd.py: -------------------------------------------------------------------------------- 1 | # (c) Copyright 2014,2015 Hewlett-Packard Development Company, L.P. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import subprocess 16 | import unittest 17 | from unittest import mock 18 | from unittest.mock import patch 19 | 20 | from freezer.utils import exec_cmd 21 | 22 | 23 | class TestExec(unittest.TestCase): 24 | def test_exec_cmd(self): 25 | cmd = "echo test > test.txt" 26 | popen = patch('freezer.utils.exec_cmd.subprocess.Popen') 27 | mock_popen = popen.start() 28 | mock_popen.return_value = mock.Mock() 29 | mock_popen.return_value.communicate = mock.Mock() 30 | mock_popen.return_value.communicate.return_value = ['some stderr'] 31 | mock_popen.return_value.returncode = 0 32 | exec_cmd.execute(cmd) 33 | assert (mock_popen.call_count == 1) 34 | mock_popen.assert_called_with(['echo', 'test', '>', 'test.txt'], 35 | shell=False, 36 | stderr=subprocess.PIPE, 37 | stdout=subprocess.PIPE) 38 | popen.stop() 39 | 40 | def test__exec_cmd_with_pipe(self): 41 | cmd = "echo test|wc -l" 42 | popen = patch('freezer.utils.exec_cmd.subprocess.Popen') 43 | mock_popen = popen.start() 44 | mock_popen.return_value = mock.Mock() 45 | mock_popen.return_value.communicate = mock.Mock() 46 | mock_popen.return_value.communicate.return_value = ['some stderr'] 47 | mock_popen.return_value.returncode = 0 48 | exec_cmd.execute(cmd) 49 | assert (mock_popen.call_count == 2) 50 | popen.stop() 51 | -------------------------------------------------------------------------------- /freezer/tests/unit/utils/test_streaming.py: -------------------------------------------------------------------------------- 1 | # (c) Copyright 2020 ZTE Corporation. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import queue 16 | import unittest 17 | 18 | from freezer.utils import streaming 19 | 20 | 21 | class StreamingTestCase(unittest.TestCase): 22 | 23 | def create_fifo(self, size): 24 | input_queue = streaming.RichQueue(size) 25 | read_except_queue = queue.Queue() 26 | write_except_queue = queue.Queue() 27 | 28 | read_stream = streaming.QueuedThread(self.backup_stream, 29 | input_queue, 30 | read_except_queue) 31 | 32 | write_stream = streaming.QueuedThread(self.write_stream, 33 | input_queue, 34 | write_except_queue) 35 | 36 | read_stream.daemon = True 37 | write_stream.daemon = True 38 | read_stream.start() 39 | write_stream.start() 40 | read_stream.join() 41 | write_stream.join() 42 | 43 | return input_queue 44 | 45 | def backup_stream(self, rich_queue): 46 | rich_queue.put_messages('ancd') 47 | 48 | def write_stream(self, rich_queue): 49 | for message in rich_queue.get_messages(): 50 | pass 51 | 52 | def test_stream_1(self): 53 | input_queue = self.create_fifo(1) 54 | assert input_queue.finish_transmission is True 55 | 56 | def test_stream_2(self): 57 | input_queue = self.create_fifo(2) 58 | assert input_queue.finish_transmission is True 59 | 60 | def test_stream_3(self): 61 | input_queue = self.create_fifo(3) 62 | assert input_queue.finish_transmission is True 63 | 64 | def test_stream_4(self): 65 | input_queue = self.create_fifo(4) 66 | assert input_queue.finish_transmission is True 67 | 68 | def test_stream_5(self): 69 | input_queue = self.create_fifo(5) 70 | assert input_queue.finish_transmission is True 71 | -------------------------------------------------------------------------------- /freezer/tests/unit/utils/test_winutils.py: -------------------------------------------------------------------------------- 1 | # (c) Copyright 2014,2015 Hewlett-Packard Development Company, L.P. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import os 16 | import unittest 17 | from unittest import mock 18 | 19 | from freezer.tests import commons 20 | from freezer.utils import winutils 21 | 22 | 23 | class TestWinutils(unittest.TestCase): 24 | 25 | def mock_process(self, process): 26 | fakesubprocesspopen = process.Popen() 27 | mock.patch('subprocess.Popen.communicate', 28 | new_callable=fakesubprocesspopen.communicate).start() 29 | mock.patch('subprocess.Popen', new_callable=fakesubprocesspopen)\ 30 | .start() 31 | 32 | def mock_winutils(self): 33 | fake_disable_redirection = commons.FakeDisableFileSystemRedirection() 34 | mock.patch('winutils.DisableFileSystemRedirection.__enter__', 35 | new_callable=fake_disable_redirection.__enter__) 36 | mock.patch('winutils.DisableFileSystemRedirection.__exit__', 37 | new_callable=fake_disable_redirection.__exit__) 38 | 39 | def test_is_windows(self): 40 | fake_os = commons.Os() 41 | os.name = fake_os 42 | assert winutils.is_windows() is False 43 | 44 | def test_use_shadow(self): 45 | test_volume = 'C:' 46 | test_volume2 = 'C:\\' 47 | path = 'C:\\Users\\Test' 48 | expected = 'C:\\freezer_shadowcopy\\Users\\Test' 49 | assert winutils.use_shadow(path, test_volume2) == expected 50 | 51 | # test if the volume format is incorrect 52 | self.assertRaises(Exception, 53 | winutils.use_shadow(path, test_volume)) # noqa 54 | 55 | # def test_start_sql_server(self): 56 | # backup_opt = BackupOpt1() 57 | # self.mock_process(FakeSubProcess()) 58 | # self.mock_winutils() 59 | # 60 | # assert winutils.start_sql_server( 61 | # backup_opt.sql_server_instance) is not False 62 | # 63 | # self.mock_process(FakeSubProcess3()) 64 | # self.assertRaises( 65 | # Exception, 66 | # winutils.start_sql_server(backup_opt.sql_server_instance)) 67 | # 68 | # def test_stop_sql_server(self): 69 | # backup_opt = BackupOpt1() 70 | # self.mock_process(FakeSubProcess()) 71 | # self.mock_winutils() 72 | # 73 | # assert winutils.start_sql_server( 74 | # backup_opt.sql_server_instance) is not False 75 | # 76 | # self.mock_process(FakeSubProcess3()) 77 | # 78 | # self.assertRaises(Exception, winutils.stop_sql_server( 79 | # backup_opt.sql_server_instance)) 80 | -------------------------------------------------------------------------------- /freezer/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/freezer/f0e435038467d1fe284ec4b8e87f70bfaa41f490/freezer/utils/__init__.py -------------------------------------------------------------------------------- /freezer/utils/compress.py: -------------------------------------------------------------------------------- 1 | # (C) Copyright 2016 Mirantis, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | # not use this file except in compliance with the License. You may obtain 5 | # a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | # License for the specific language governing permissions and limitations 13 | # under the License. 14 | 15 | GZIP = 'zlib' 16 | BZIP2 = 'bz2' 17 | XZ = 'lzma' 18 | 19 | COMPRESS_METHOD = 'compress' 20 | DECOMPRESS_METHOD = 'decompress' 21 | 22 | 23 | def get_compression_algo(compression_algo): 24 | algo = { 25 | 'gzip': GZIP, 26 | 'bzip2': BZIP2, 27 | 'xz': XZ, 28 | } 29 | return algo.get(compression_algo) 30 | 31 | 32 | def one_shot_compress(compression_algo, data): 33 | compression_module = __import__(get_compression_algo(compression_algo)) 34 | return getattr(compression_module, COMPRESS_METHOD)(data) 35 | 36 | 37 | def one_shot_decompress(compression_algo, data): 38 | compression_module = __import__(get_compression_algo(compression_algo)) 39 | return getattr(compression_module, DECOMPRESS_METHOD)(data) 40 | 41 | 42 | class BaseCompressor(object): 43 | """ 44 | Base class for compress/decompress activities. 45 | """ 46 | 47 | def __init__(self, compression_algo): 48 | # TODO(raliev): lzma module exists in stdlib since Py3 only 49 | if compression_algo == 'xz': 50 | raise NotImplementedError('XZ compression not implemented yet') 51 | self.algo = get_compression_algo(compression_algo) 52 | self.module = __import__(self.algo) 53 | 54 | 55 | class Compressor(BaseCompressor): 56 | """ 57 | Compress chucks of data. 58 | """ 59 | 60 | MAX_COMPRESS_LEVEL = 9 61 | 62 | def __init__(self, compression_algo): 63 | super(Compressor, self).__init__(compression_algo) 64 | self.compressobj = self.create_compressobj(compression_algo) 65 | 66 | def create_compressobj(self, compression_algo): 67 | def get_obj_name(): 68 | names = { 69 | 'gzip': 'compressobj', 70 | 'bzip2': 'BZ2Compressor', 71 | 'xz': 'compressobj', 72 | } 73 | return names.get(compression_algo) 74 | 75 | obj_name = get_obj_name() 76 | return getattr(self.module, obj_name)(self.MAX_COMPRESS_LEVEL) 77 | 78 | def compress(self, data): 79 | return self.compressobj.compress(data) 80 | 81 | def flush(self): 82 | return self.compressobj.flush() 83 | 84 | 85 | class Decompressor(BaseCompressor): 86 | """ 87 | Decompress chucks of data. 88 | """ 89 | 90 | def __init__(self, compression_algo): 91 | super(Decompressor, self).__init__(compression_algo) 92 | self.decompressobj = self.create_decompressobj(compression_algo) 93 | 94 | def create_decompressobj(self, compression_algo): 95 | def get_obj_name(): 96 | names = { 97 | 'gzip': 'decompressobj', 98 | 'bzip2': 'BZ2Decompressor', 99 | 'xz': 'decompressobj', 100 | } 101 | return names.get(compression_algo) 102 | 103 | obj_name = get_obj_name() 104 | return getattr(self.module, obj_name)() 105 | 106 | def decompress(self, data): 107 | return self.decompressobj.decompress(data) 108 | 109 | def flush(self): 110 | return self.decompressobj.flush() 111 | -------------------------------------------------------------------------------- /freezer/utils/config.py: -------------------------------------------------------------------------------- 1 | # (c) Copyright 2014,2015 Hewlett-Packard Development Company, L.P. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import configparser 16 | import os 17 | import re 18 | 19 | from oslo_log import log 20 | 21 | from freezer.utils import utils 22 | 23 | LOG = log.getLogger(__name__) 24 | 25 | 26 | EXPORT = re.compile(r"^\s*export\s+([^=^#^\s]+)\s*=\s*([^#^\n]*)\s*$", 27 | re.MULTILINE) 28 | 29 | INI = re.compile(r"^\s*([^=#\s]+)\s*=[\t]*([^#\n]*)\s*$", re.MULTILINE) 30 | 31 | 32 | class Config(object): 33 | 34 | @staticmethod 35 | def parse(config_path): 36 | if config_path: 37 | if not os.path.exists(config_path): 38 | LOG.error("Critical Error: Configuration file {0} not" 39 | " found".format(config_path)) 40 | raise Exception("Configuration file {0} not found !".format( 41 | config_path)) 42 | # SafeConfigParser was deprecated in Python 3.2 43 | config = configparser.ConfigParser() 44 | config.read([config_path]) 45 | sections = config.sections() 46 | storages = [] 47 | default_options = {} 48 | for section in sections: 49 | dict = {} 50 | for option in config.options(section): 51 | option_value = config.get(section, option) 52 | if option_value in ('False', 'false', 'None'): 53 | option_value = False 54 | elif option_value in ('True', 'true'): 55 | option_value = True 56 | dict[option] = option_value 57 | if section.startswith("storage:"): 58 | storages.append(dict) 59 | else: 60 | default_options.update(dict) 61 | return Config(default_options, storages) 62 | 63 | def __init__(self, default, storages): 64 | """ 65 | :param default: 66 | :type default: dict 67 | :param storages: 68 | :type storages: list[dict] 69 | :return: 70 | """ 71 | self.default = default 72 | self.storages = storages 73 | 74 | 75 | def osrc_parse(lines): 76 | """ 77 | :param lines: 78 | :type lines: str 79 | :return: 80 | """ 81 | return find_all(EXPORT, lines) 82 | 83 | 84 | def ini_parse(fd): 85 | """ 86 | :param fd: 87 | :type fd: file_descriptor 88 | :return: 89 | """ 90 | parser = configparser.ConfigParser() 91 | parser.read_file(fd) 92 | return dict(parser.items('default')) 93 | 94 | 95 | def find_all(regex, lines): 96 | return dict([(k.strip(), utils.dequote(v.strip())) for k, v in 97 | regex.findall(lines)]) 98 | -------------------------------------------------------------------------------- /freezer/utils/crypt.py: -------------------------------------------------------------------------------- 1 | # (C) Copyright 2016 Mirantis, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | # not use this file except in compliance with the License. You may obtain 5 | # a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | # License for the specific language governing permissions and limitations 13 | # under the License. 14 | 15 | import hashlib 16 | 17 | from cryptography.hazmat.backends import default_backend 18 | from cryptography.hazmat.primitives.ciphers import algorithms 19 | from cryptography.hazmat.primitives.ciphers import Cipher 20 | from cryptography.hazmat.primitives.ciphers import modes 21 | from os import urandom 22 | 23 | SALT_HEADER = 'Salted__' 24 | AES256_KEY_LENGTH = 32 25 | BS = 16 # static 16 bytes, 128 bits for AES 26 | 27 | 28 | class AESCipher(object): 29 | """ 30 | Base class for encrypt/decrypt activities. 31 | """ 32 | 33 | def __init__(self, pass_file): 34 | self._password = self._get_pass_from_file(pass_file) 35 | self._salt = None 36 | self.cipher = None 37 | 38 | @staticmethod 39 | def _get_pass_from_file(pass_file): 40 | with open(pass_file) as p_file: 41 | password = p_file.readline() 42 | return password 43 | 44 | @staticmethod 45 | def _derive_key_and_iv(password, salt, key_length, iv_length): 46 | d = d_i = b'' 47 | while len(d) < key_length + iv_length: 48 | md5_str = d_i + password + salt 49 | d_i = hashlib.md5(md5_str).digest() 50 | d += d_i 51 | return d[:key_length], d[key_length:key_length + iv_length] 52 | 53 | 54 | class AESEncrypt(AESCipher): 55 | """ 56 | Encrypts chucks of data using AES-256 algorithm. 57 | OpenSSL compatible. 58 | """ 59 | 60 | def __init__(self, pass_file): 61 | super(AESEncrypt, self).__init__(pass_file) 62 | self._salt = urandom(BS - len(SALT_HEADER)) 63 | key, iv = self._derive_key_and_iv(self._password, 64 | self._salt, 65 | AES256_KEY_LENGTH, 66 | BS) 67 | self.cipher = Cipher(algorithms.AES(key), 68 | modes.CFB(iv), 69 | backend=default_backend()) 70 | 71 | def generate_header(self): 72 | return SALT_HEADER + self._salt 73 | 74 | def encrypt(self, data): 75 | encryptor = self.cipher.encryptor() 76 | return encryptor.update(data) + encryptor.finalize() 77 | 78 | 79 | class AESDecrypt(AESCipher): 80 | """ 81 | Decrypts chucks of data using AES-256 algorithm. 82 | OpenSSL compatible. 83 | """ 84 | 85 | def __init__(self, pass_file, salt): 86 | super(AESDecrypt, self).__init__(pass_file) 87 | self._salt = self._prepare_salt(salt) 88 | key, iv = self._derive_key_and_iv(self._password, 89 | self._salt, 90 | AES256_KEY_LENGTH, 91 | BS) 92 | self.cipher = Cipher(algorithms.AES(key), 93 | modes.CFB(iv), 94 | backend=default_backend()) 95 | 96 | @staticmethod 97 | def _prepare_salt(salt): 98 | return salt[len(SALT_HEADER):] 99 | 100 | def decrypt(self, data): 101 | decryptor = self.cipher.decryptor() 102 | return decryptor.update(data) + decryptor.finalize() 103 | -------------------------------------------------------------------------------- /freezer/utils/exec_cmd.py: -------------------------------------------------------------------------------- 1 | # (c) Copyright 2014,2015 Hewlett-Packard Development Company, L.P. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import subprocess 16 | 17 | 18 | def execute(cmd): 19 | """ 20 | Split a command specified as function arguments into separate sub commands 21 | executed separately 22 | """ 23 | cmds = cmd.split('|') 24 | nb_process = len(cmds) 25 | index = 1 26 | process = None 27 | for sub_cmd in cmds: 28 | is_last_process = (index == nb_process) 29 | process = popen_call(sub_cmd.split(' '), process, is_last_process) 30 | index += 1 31 | 32 | 33 | def popen_call(sub_cmd, input, is_last_process): 34 | """ 35 | Execute a command specified as function arguments using the given input 36 | """ 37 | if not input: 38 | process = subprocess.Popen(sub_cmd, 39 | stdout=subprocess.PIPE, 40 | stderr=subprocess.PIPE, shell=False) 41 | else: 42 | process = subprocess.Popen(sub_cmd, 43 | stdout=subprocess.PIPE, stdin=input.stdout, 44 | stderr=subprocess.PIPE, shell=False) 45 | input.stdout.close() 46 | if (is_last_process): 47 | process.communicate()[0] 48 | rc = process.returncode 49 | if rc != 0: 50 | raise Exception('Error: while executing script ' 51 | '%s return code was %d instead of 0' 52 | % (''.join(sub_cmd), rc)) 53 | return process 54 | -------------------------------------------------------------------------------- /freezer/utils/streaming.py: -------------------------------------------------------------------------------- 1 | # (c) Copyright 2014,2015 Hewlett-Packard Development Company, L.P. 2 | # (c) Copyright 2016 Hewlett-Packard Enterprise Development Company, L.P. 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | 16 | """ 17 | Freezer general utils functions 18 | """ 19 | 20 | import threading 21 | 22 | from oslo_log import log 23 | import queue 24 | 25 | 26 | LOG = log.getLogger(__name__) 27 | 28 | 29 | class Wait(Exception): 30 | pass 31 | 32 | 33 | class RichQueue(object): 34 | """ 35 | :type data_queue: Queue.Queue 36 | """ 37 | def __init__(self, size=2): 38 | """ 39 | :type size: int 40 | :return: 41 | """ 42 | self.data_queue = queue.Queue(maxsize=size) 43 | # transmission changes in atomic way so no synchronization needed 44 | self.finish_transmission = False 45 | self.is_force_stop = False 46 | 47 | def finish(self): 48 | self.finish_transmission = True 49 | 50 | def force_stop(self): 51 | self.is_force_stop = True 52 | 53 | def empty(self): 54 | return self.data_queue.empty() 55 | 56 | def get(self): 57 | try: 58 | res = self.data_queue.get(timeout=1) 59 | self.data_queue.task_done() 60 | return res 61 | except queue.Empty: 62 | raise Wait() 63 | 64 | def check_stop(self): 65 | if self.is_force_stop: 66 | raise Exception("Forced stop") 67 | 68 | def put_messages(self, messages): 69 | for message in messages: 70 | self.put(message) 71 | self.finish() 72 | 73 | def has_more(self): 74 | self.check_stop() 75 | return not self.finish_transmission or not self.data_queue.empty() 76 | 77 | def put(self, message): 78 | while True: 79 | try: 80 | self.data_queue.put(message, timeout=1) 81 | break 82 | except queue.Full: 83 | self.check_stop() 84 | 85 | def get_messages(self): 86 | while self.has_more(): 87 | try: 88 | yield self.get() 89 | except Wait: 90 | self.check_stop() 91 | 92 | 93 | class QueuedThread(threading.Thread): 94 | def __init__(self, target, rich_queue, exception_queue, 95 | args=(), kwargs=None): 96 | """ 97 | :type args: collections.Iterable 98 | :type kwargs: dict 99 | :type target: () -> () 100 | :type rich_queue: RichQueue 101 | """ 102 | self.args = args 103 | kwargs = kwargs or {} 104 | self.rich_queue = rich_queue 105 | self._exception_queue = exception_queue 106 | kwargs["rich_queue"] = rich_queue 107 | super(QueuedThread, self).__init__(target=target, args=args, 108 | kwargs=kwargs) 109 | 110 | def run(self): 111 | try: 112 | super(QueuedThread, self).run() 113 | except Exception as e: 114 | LOG.exception(e) 115 | self._exception_queue.put_nowait(e) 116 | self.rich_queue.force_stop() 117 | # Thread will exit at this point. 118 | # @todo print the error using traceback.print_exc(file=sys.stdout) 119 | raise 120 | -------------------------------------------------------------------------------- /freezer/utils/winutils.py: -------------------------------------------------------------------------------- 1 | # (c) Copyright 2014,2015 Hewlett-Packard Development Company, L.P. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import ctypes 16 | import os 17 | import sys 18 | 19 | from oslo_serialization import jsonutils as json 20 | 21 | 22 | def is_windows(): 23 | """ 24 | :return: True if the running platform is windows 25 | """ 26 | return True if sys.platform == 'win32' else False 27 | 28 | 29 | class DisableFileSystemRedirection(object): 30 | """ 31 | When a 32 bit program runs on a 64 bit operating system the paths 32 | to C:/Windows/System32 automatically get redirected to the 32 bit 33 | version (C:/Windows/SysWow64), if you really do need to access the 34 | contents of System32, you need to disable the file system redirector first. 35 | """ 36 | 37 | def __init__(self): 38 | if is_windows(): 39 | self._disable = (ctypes.windll.kernel32. 40 | Wow64DisableWow64FsRedirection) 41 | self._revert = (ctypes.windll.kernel32. 42 | Wow64RevertWow64FsRedirection) 43 | else: 44 | raise Exception("Useless if not windows") 45 | 46 | def __enter__(self): 47 | self.old_value = ctypes.c_long() 48 | self.success = self._disable(ctypes.byref(self.old_value)) 49 | 50 | def __exit__(self, type, value, traceback): 51 | if self.success: 52 | self._revert(self.old_value) 53 | 54 | 55 | def use_shadow(to_backup, windows_volume): 56 | """ add the shadow path to the backup directory """ 57 | return to_backup.replace(windows_volume, '{0}freezer_shadowcopy\\' 58 | .format(windows_volume)) 59 | 60 | 61 | def save_environment(home): 62 | """Read the environment from the terminal where the scheduler is 63 | initialized and save the environment variables to be reused within the 64 | windows service 65 | """ 66 | env_path = os.path.join(home, 'env.json') 67 | with open(env_path, 'wb') as tmp: 68 | json.dump(os.environ.copy(), tmp) 69 | 70 | 71 | def set_environment(home): 72 | """Read the environment variables saved by the win_daemon and restore it 73 | here for the windows service 74 | """ 75 | json_env = os.path.join(home, 'env.json') 76 | with open(json_env, 'rb') as fp: 77 | env = json.loads(fp.read()) 78 | for k, v in env.items(): 79 | os.environ[str(k).strip()] = str(v).strip() 80 | -------------------------------------------------------------------------------- /freezer_logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/freezer/f0e435038467d1fe284ec4b8e87f70bfaa41f490/freezer_logo.jpg -------------------------------------------------------------------------------- /releasenotes/notes/drop-py-2-7-a76d53b7a12bcff2.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | upgrade: 3 | - | 4 | Python 2.7 support has been dropped. Last release of freezer 5 | to support py2.7 is OpenStack Train. The minimum version of Python now 6 | supported by freezer is Python 3.6. 7 | -------------------------------------------------------------------------------- /releasenotes/notes/os-brick-engine-c47834de156dfa27.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | prelude: > 3 | Currently Freezer provides basic features to execute Cinder volumes backup. 4 | The current approach present significant challenges, 5 | due mainly to the difficulty of downloading Cinder Volumes without passing 6 | through Glance. This can be an issue for time and scalability reasons, 7 | (i.e. volumes of few hundreds GB size, potential error probability increase, 8 | as more services are part of the process, unailability of cinder-backup). 9 | 10 | features: 11 | - | 12 | Added new backup engine called 'os-brick' which allows to backup and 13 | restore the content of cinder volumes attaching it directly to localhost 14 | using functionality of os-brick library. 15 | 16 | issues: 17 | - | 18 | There are could be a read/write file permisson issues if freezer-agent 19 | don't have appropriate right to read\write files to mounted FS. 20 | -------------------------------------------------------------------------------- /releasenotes/notes/s3-driver-support-02d0a19b99cfe2c5.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | prelude: > 3 | Currently, freezer can only store backup data to swift compatible object 4 | storage (except local and ssh), so we should increase support for other 5 | storage driver. S3 compatible object storage is a valid choice, which is 6 | used by many individuals and companies in the public or private clouds. 7 | 8 | features: 9 | - | 10 | Added new storage type called 's3' which allows to store the backup data 11 | to S3 compatible object storage and restore from it with using botocore 12 | library. 13 | -------------------------------------------------------------------------------- /releasenotes/notes/volume-boot-nova-instance-backup-support-3c8d090370518f43.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | prelude: > 3 | Currently, when using 'freezer-agent --action backup --engine nova 4 | --nova-inst-id xxx --mode nova --no-incremental true' to backup instance 5 | that boot from volume or snapshot, it gives us the result of successful 6 | backup. But when we restore the nova instance from the backup data and 7 | launch the restored instance, it will fail with 'no boot device error' 8 | message. This can be an issue. 9 | 10 | fixes: 11 | - | 12 | With the above issue, freezer can not support the backup and restore of 13 | instance that boot from volume or snapshot correctly. With this fix, when 14 | using backup, freezer will create an image from the volume, and then 15 | store the image data to storage media. After this fix, users can backup 16 | and restore the nova instance no matter what type of the instance is. 17 | -------------------------------------------------------------------------------- /releasenotes/source/2023.1.rst: -------------------------------------------------------------------------------- 1 | =========================== 2 | 2023.1 Series Release Notes 3 | =========================== 4 | 5 | .. release-notes:: 6 | :branch: stable/2023.1 7 | -------------------------------------------------------------------------------- /releasenotes/source/2023.2.rst: -------------------------------------------------------------------------------- 1 | =========================== 2 | 2023.2 Series Release Notes 3 | =========================== 4 | 5 | .. release-notes:: 6 | :branch: stable/2023.2 7 | -------------------------------------------------------------------------------- /releasenotes/source/2025.1.rst: -------------------------------------------------------------------------------- 1 | =========================== 2 | 2025.1 Series Release Notes 3 | =========================== 4 | 5 | .. release-notes:: 6 | :branch: stable/2025.1 7 | -------------------------------------------------------------------------------- /releasenotes/source/_static/.placeholder: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/freezer/f0e435038467d1fe284ec4b8e87f70bfaa41f490/releasenotes/source/_static/.placeholder -------------------------------------------------------------------------------- /releasenotes/source/_templates/.placeholder: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/freezer/f0e435038467d1fe284ec4b8e87f70bfaa41f490/releasenotes/source/_templates/.placeholder -------------------------------------------------------------------------------- /releasenotes/source/index.rst: -------------------------------------------------------------------------------- 1 | .. 2 | Licensed under the Apache License, Version 2.0 (the "License"); you may 3 | not use this file except in compliance with the License. You may obtain 4 | a copy of the License at 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 10 | WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 11 | License for the specific language governing permissions and limitations 12 | under the License. 13 | 14 | ===================================================== 15 | Welcome to Freezer Agent Release Notes documentation! 16 | ===================================================== 17 | 18 | Contents 19 | ======== 20 | 21 | .. toctree:: 22 | :maxdepth: 2 23 | 24 | unreleased 25 | 2025.1 26 | 2023.2 27 | 2023.1 28 | zed 29 | yoga 30 | wallaby 31 | victoria 32 | ussuri 33 | train 34 | stein 35 | queens 36 | pike 37 | ocata 38 | newton 39 | 40 | Indices and tables 41 | ================== 42 | 43 | * :ref:`genindex` 44 | * :ref:`search` 45 | -------------------------------------------------------------------------------- /releasenotes/source/newton.rst: -------------------------------------------------------------------------------- 1 | ============================ 2 | Newton Series Release Notes 3 | ============================ 4 | 5 | .. release-notes:: 6 | :branch: stable/newton 7 | -------------------------------------------------------------------------------- /releasenotes/source/ocata.rst: -------------------------------------------------------------------------------- 1 | =================================== 2 | Ocata Series Release Notes 3 | =================================== 4 | 5 | .. release-notes:: 6 | :branch: origin/stable/ocata 7 | -------------------------------------------------------------------------------- /releasenotes/source/pike.rst: -------------------------------------------------------------------------------- 1 | =================================== 2 | Pike Series Release Notes 3 | =================================== 4 | 5 | .. release-notes:: 6 | :branch: stable/pike 7 | -------------------------------------------------------------------------------- /releasenotes/source/queens.rst: -------------------------------------------------------------------------------- 1 | =================================== 2 | Queens Series Release Notes 3 | =================================== 4 | 5 | .. release-notes:: 6 | :branch: stable/queens 7 | -------------------------------------------------------------------------------- /releasenotes/source/stein.rst: -------------------------------------------------------------------------------- 1 | =================================== 2 | Stein Series Release Notes 3 | =================================== 4 | 5 | .. release-notes:: 6 | :branch: stable/stein 7 | -------------------------------------------------------------------------------- /releasenotes/source/train.rst: -------------------------------------------------------------------------------- 1 | ========================== 2 | Train Series Release Notes 3 | ========================== 4 | 5 | .. release-notes:: 6 | :branch: stable/train 7 | -------------------------------------------------------------------------------- /releasenotes/source/unreleased.rst: -------------------------------------------------------------------------------- 1 | ============================ 2 | Current Series Release Notes 3 | ============================ 4 | 5 | .. release-notes:: 6 | -------------------------------------------------------------------------------- /releasenotes/source/ussuri.rst: -------------------------------------------------------------------------------- 1 | =========================== 2 | Ussuri Series Release Notes 3 | =========================== 4 | 5 | .. release-notes:: 6 | :branch: stable/ussuri 7 | -------------------------------------------------------------------------------- /releasenotes/source/victoria.rst: -------------------------------------------------------------------------------- 1 | ============================= 2 | Victoria Series Release Notes 3 | ============================= 4 | 5 | .. release-notes:: 6 | :branch: stable/victoria 7 | -------------------------------------------------------------------------------- /releasenotes/source/wallaby.rst: -------------------------------------------------------------------------------- 1 | ============================ 2 | Wallaby Series Release Notes 3 | ============================ 4 | 5 | .. release-notes:: 6 | :branch: stable/wallaby 7 | -------------------------------------------------------------------------------- /releasenotes/source/yoga.rst: -------------------------------------------------------------------------------- 1 | ========================= 2 | Yoga Series Release Notes 3 | ========================= 4 | 5 | .. release-notes:: 6 | :branch: unmaintained/yoga 7 | -------------------------------------------------------------------------------- /releasenotes/source/zed.rst: -------------------------------------------------------------------------------- 1 | ======================== 2 | Zed Series Release Notes 3 | ======================== 4 | 5 | .. release-notes:: 6 | :branch: unmaintained/zed 7 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | # Requirements lower bounds listed here are our best effort to keep them up to 2 | # date but we do not test them so no guarantee of having them all correct. If 3 | # you find any incorrect lower bounds, let us know or propose a fix. 4 | pbr>=4.0.0 # Apache-2.0 5 | 6 | botocore>=1.5.1 # Apache-2.0 7 | setuptools!=24.0.0,!=34.0.0,!=34.0.1,!=34.0.2,!=34.0.3,!=34.1.0,!=34.1.1,!=34.2.0,!=34.3.0,!=34.3.1,!=34.3.2,!=36.2.0,>=21.0.0 # PSF/ZPL 8 | python-swiftclient>=3.2.0 # Apache-2.0 9 | python-cinderclient>=3.3.0 # Apache-2.0 10 | python-glanceclient>=2.8.0 # Apache-2.0 11 | python-keystoneclient>=3.8.0 # Apache-2.0 12 | python-novaclient>=9.1.0 # Apache-2.0 13 | python-neutronclient>=6.7.0 # Apache-2.0 14 | python-freezerclient>=2.0.0 # Apache-2.0 15 | oslo.serialization>=5.5.0 # Apache-2.0 16 | oslo.utils>=7.3.0 # Apache-2.0 17 | oslo.log>=5.3.0 # Apache-2.0 18 | oslo.config>=9.4.0 # Apache-2.0 19 | oslo.service!=1.28.1,>=1.24.0 # Apache-2.0 20 | keystoneauth1>=3.14.0 # Apache-2.0 21 | os-brick>=2.2.0 # Apache-2.0 22 | 23 | cryptography>=2.5 # Apache-2.0 24 | PyMySQL>=0.7.6 # MIT License 25 | pymongo!=3.1,>=3.0.2 # Apache-2.0 26 | paramiko>=2.7.1 # LGPLv2.1+ 27 | 28 | apscheduler>=3.0.5 # MIT License 29 | psutil>=3.2.2 # BSD 30 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | name = freezer 3 | author = OpenStack 4 | author_email = openstack-discuss@lists.openstack.org 5 | summary = The OpenStack Backup and Restore as a Service Platform 6 | description_file = README.rst 7 | description-content-type = text/x-rst 8 | home_page = https://docs.openstack.org/freezer/latest/ 9 | license = Apache-2 10 | python_requires = >=3.9 11 | classifier = 12 | Programming Language :: Python 13 | Programming Language :: Python :: Implementation :: CPython 14 | Programming Language :: Python :: 3 :: Only 15 | Programming Language :: Python :: 3 16 | Programming Language :: Python :: 3.9 17 | Programming Language :: Python :: 3.10 18 | Programming Language :: Python :: 3.11 19 | Programming Language :: Python :: 3.12 20 | Development Status :: 5 - Production/Stable 21 | Natural Language :: English 22 | Environment :: OpenStack 23 | Intended Audience :: Developers 24 | Intended Audience :: Information Technology 25 | Intended Audience :: System Administrators 26 | License :: OSI Approved :: Apache Software License 27 | Operating System :: MacOS 28 | Operating System :: POSIX :: BSD :: FreeBSD 29 | Operating System :: POSIX :: BSD :: NetBSD 30 | Operating System :: POSIX :: BSD :: OpenBSD 31 | Operating System :: POSIX :: Linux 32 | Operating System :: Microsoft :: Windows 33 | Operating System :: Unix 34 | Topic :: System :: Archiving :: Backup 35 | Topic :: System :: Archiving :: Compression 36 | Topic :: System :: Archiving 37 | keywords = 38 | freezer 39 | backup 40 | openstack 41 | restore 42 | lvm 43 | snapshot 44 | mongodb 45 | mysql 46 | 47 | [global] 48 | setup_hooks = 49 | pbr.hooks.setup_hook 50 | 51 | [files] 52 | packages = 53 | freezer 54 | data_files = 55 | freezer/scripts = freezer/scripts/vss.ps1 56 | 57 | [entry_points] 58 | oslo.config.opts = 59 | freezer-agent = freezer.common.config:list_opts 60 | freezer-scheduler = freezer.scheduler.arguments:list_opts 61 | console_scripts = 62 | freezer-scheduler = freezer.scheduler.freezer_scheduler:main 63 | freezer-agent = freezer.main:main 64 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2013 Hewlett-Packard Development Company, L.P. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 12 | # implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | import setuptools 17 | 18 | setuptools.setup( 19 | setup_requires=['pbr>=2.0.0'], 20 | pbr=True) 21 | -------------------------------------------------------------------------------- /test-requirements.txt: -------------------------------------------------------------------------------- 1 | # The order of packages is significant, because pip processes them in the order 2 | # of appearance. Changing the order has an impact on the overall integration 3 | # process, which may cause wedges in the gate later. 4 | 5 | hacking>=3.0.1,<3.1.0 # Apache-2.0 6 | coverage>=4.5.1 # Apache-2.0 7 | ddt>=1.0.1 # MIT 8 | #pylint==1.9.2 # GPLv2 9 | stestr>=2.0.0 # Apache-2.0 10 | testtools>=2.2.0 # MIT 11 | #astroid==1.6.5 # LGPLv2.1 12 | 13 | # Tempest Plugin 14 | tempest>=17.1.0 # Apache-2.0 15 | 16 | # Used in integration tests 17 | python-openstackclient>=3.12.0 # Apache-2.0 18 | 19 | # Used in doc8 check 20 | doc8>=0.6.0 # Apache-2.0 21 | Pygments>=2.2.0 # BSD license 22 | # astroid<=2.5.0;python_version>="3.0" # LGPLv2.1 23 | pylint>=2.6.0 # GPLv2 24 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = py3,pep8,pylint,docs 3 | skipsdist = True 4 | 5 | [testenv] 6 | usedevelop = True 7 | deps = 8 | -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} 9 | -r{toxinidir}/requirements.txt 10 | -r{toxinidir}/test-requirements.txt 11 | 12 | passenv = 13 | FREEZER_TEST_SSH_KEY 14 | FREEZER_TEST_SSH_USERNAME 15 | FREEZER_TEST_SSH_HOST 16 | FREEZER_TEST_CONTAINER 17 | FREEZER_TEST_OS_PROJECT_NAME 18 | FREEZER_TEST_OS_USERNAME 19 | FREEZER_TEST_OS_REGION_NAME 20 | FREEZER_TEST_OS_PASSWORD 21 | FREEZER_TEST_OS_AUTH_URL 22 | FREEZER_TEST_NO_LVM 23 | http_proxy 24 | HTTP_PROXY 25 | https_proxy 26 | HTTPS_PROXY 27 | no_proxy 28 | NO_PROXY 29 | 30 | install_command = pip3 install {opts} {packages} 31 | setenv = 32 | VIRTUAL_ENV={envdir} 33 | OS_TEST_PATH = ./freezer/tests/unit 34 | PYTHON=coverage run --source freezer --parallel-mode 35 | commands = 36 | find . -type f -name "*.pyc" -delete 37 | stestr run {posargs} 38 | coverage combine 39 | coverage html -d cover 40 | coverage xml -o cover/coverage.xml 41 | coverage report -m 42 | rm -f .coverage 43 | rm -rf .testrepository 44 | 45 | allowlist_externals = 46 | find 47 | coverage 48 | rm 49 | 50 | python_files = test_*.py 51 | norecursedirs = .tox .venv 52 | 53 | [testenv:venv] 54 | commands = {posargs} 55 | 56 | [testenv:py39] 57 | basepython = python3.9 58 | 59 | [testenv:py312] 60 | basepython = python3.12 61 | 62 | [testenv:docs] 63 | deps = 64 | -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} 65 | -r{toxinidir}/doc/requirements.txt 66 | allowlist_externals = rm 67 | commands = 68 | rm -rf doc/build/ 69 | sphinx-build --keep-going -b html doc/source doc/build/html 70 | 71 | 72 | [testenv:pep8] 73 | commands = 74 | flake8 freezer 75 | doc8 {posargs} 76 | 77 | [testenv:pylint] 78 | commands = pylint --rcfile .pylintrc freezer 79 | 80 | [flake8] 81 | # W504 line break after binary operator 82 | # W605 invalid escape sequence 83 | 84 | ignore = H405,H404,H403,H401,W504,W605 85 | show-source = True 86 | enable-extensions = H203,H106 87 | exclude = .venv,.tox,dist,doc,test,*egg,releasenotes 88 | 89 | [doc8] 90 | ignore = D000,D001 91 | ignore-path = .venv,.git,.tox,.tmp,*freezer/locale*,*lib/python*,freezer.egg*,doc/build,releasenotes/*,doc/source/contributor/api 92 | 93 | [testenv:releasenotes]: 94 | deps = 95 | -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} 96 | -r{toxinidir}/doc/requirements.txt 97 | allowlist_externals = rm 98 | commands = 99 | rm -rf releasenotes/build 100 | sphinx-build -a -E -d releasenotes/build/doctrees --keep-going -b html releasenotes/source releasenotes/build/html 101 | --------------------------------------------------------------------------------