Skip to content
This repository has been archived by the owner on Feb 9, 2022. It is now read-only.

Add additional python scripts to provide mapping validation and automatic scripts for generate_mapping failover and failback #38

Merged
merged 9 commits into from May 2, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
23 changes: 23 additions & 0 deletions README.md
Expand Up @@ -49,6 +49,29 @@ Example Playbook
Generate var file mapping [demo](https://youtu.be/s1-Hq_Mk1w8)<br/>
Fail over scenario [demo](https://youtu.be/mEOgH-Tk09c)

Scripts
-------
The ovirt-dr script should provide the user a more convinient way to run
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ovirt-dr where is it installed in /usr/bin?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be somewhere there..

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure how to do that.
I prefer to do that on another patch so at least we will have this.
Meanwhile I've added a commit to this PR of spec file

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, np, just that user must enter full path, which is different in galaxy and in RPM installation.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should I add this in the spec as well?

disaster recovery actions as a way to avoid using ansible playbooks directly.
There are four actions which the user can execute:
validate validate the var file mapping which is used for failover and failback
generate Generate the mapping var file based on the primary and secondary setup, to be used for failover and failback
failover Start a failover process to the target setup
failback Start a failback process from the target setup to the source setup

Each of those actions is using a configuration file which its default location is oVirt.disaster-recovery/files/dr.conf

Example Script
--------------
For mapping file generation:
./ovirt-dr generate
For mapping file validation:
./ovirt-dr validate
For fail-over operation:
./ovirt-dr failover
For fail-back operation:
./ovirt-dr failback

License
-------

Expand Down
File renamed without changes.
18 changes: 18 additions & 0 deletions files/bcolors.py
@@ -0,0 +1,18 @@
#!/usr/bin/python


class bcolors:
HEADER = '\033[95m'
OKBLUE = '\033[1;34m'
OKGREEN = '\033[0;32m'
WARNING = '\x1b[0;33m'
FAIL = '\033[0;31m'
ENDC = '\033[0m'

def disable(self):
self.HEADER = ''
self.OKBLUE = ''
self.OKGREEN = ''
self.WARNING = ''
self.FAIL = ''
self.ENDC = ''
20 changes: 20 additions & 0 deletions files/dr.conf
@@ -0,0 +1,20 @@
[generate_vars]
site=http://engine.example.com/ovirt-engine/api
username=admin@internal
password=
ca_file=/etc/pki/ovirt-engine/ca.pem
output_file=/var/lib/ovirt-ansible-disaster-recovery/mapping_vars.yml
ansible_play=../examples/dr_play.yml

[validate_vars]
var_file=/var/lib/ovirt-ansible-disaster-recovery/mapping_vars.yml
vault=../examples/ovirt_passwords.yml

[failover_failback]
dr_target_host=secondary
dr_source_map=primary
vault=passwords.yml
var_file=/var/lib/ovirt-ansible-disaster-recovery/mapping_vars.yml
ansible_play=../examples/dr_play.yml
dr_report_file=/tmp/report.log

250 changes: 250 additions & 0 deletions files/fail_back.py
@@ -0,0 +1,250 @@
#!/usr/bin/python
from bcolors import bcolors
try:
import configparser
except ImportError:
import ConfigParser as configparser
import os.path
import shlex
import subprocess
import sys

from subprocess import call

INFO = bcolors.OKGREEN
INPUT = bcolors.OKGREEN
WARN = bcolors.WARNING
FAIL = bcolors.FAIL
END = bcolors.ENDC
PREFIX = "[Failback] "
PLAY_DEF = "../examples/dr_play.yml"


class FailBack():

def run(self, conf_file, log_file):
print("\n%s%sStart failback operation...%s"
% (INFO,
PREFIX,
END))
dr_tag = "fail_back"
dr_clean_tag = "clean_engine"
target_host, source_map, var_file, vault, ansible_play = \
self._init_vars(conf_file)
print("\n%s%starget_host: %s \n"
"%ssource_map: %s \n"
"%svar_file: %s \n"
"%svault: %s \n"
"%sansible_play: %s%s \n"
% (INFO,
PREFIX,
target_host,
PREFIX,
source_map,
PREFIX,
var_file,
PREFIX,
vault,
PREFIX,
ansible_play,
END))

cmd = []
cmd.append("ansible-playbook")
cmd.append(ansible_play)
cmd.append("-t")
cmd.append(dr_clean_tag)
cmd.append("-e")
cmd.append("@" + var_file)
cmd.append("-e")
cmd.append("@" + vault)
cmd.append("-e")
cmd.append(" dr_source_map=" + target_host)
cmd.append("--vault-password-file")
cmd.append("vault_secret.sh")
cmd.append("-vvv")

cmd_fb = []
cmd_fb.append("ansible-playbook")
cmd_fb.append(ansible_play)
cmd_fb.append("-t")
cmd_fb.append(dr_tag)
cmd_fb.append("-e")
cmd_fb.append("@" + var_file)
cmd_fb.append("-e")
cmd_fb.append("@" + vault)
cmd_fb.append("-e")
cmd_fb.append(" dr_target_host=" + target_host +
" dr_source_map=" + source_map)
cmd_fb.append("--vault-password-file")
cmd_fb.append("vault_secret.sh")
cmd_fb.append("-vvv")

# Setting vault password
vault_pass = raw_input(
INPUT + PREFIX + "Please enter the vault password: " + END)
os.system("export vault_password=\"" + vault_pass + "\"")
print("\n%s%sStarting cleanup process of setup '%s'"
" for oVirt ansible disaster recovery%s"
% (INFO,
PREFIX,
target_host,
END))
with open(log_file, "w") as f:
f.write("Executing cleanup command: %s" % ' '.join(map(str, cmd)))
proc = subprocess.Popen(cmd,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
for line in iter(proc.stdout.readline, ''):
# TODO: since we dont want to have log and print the
# progress in the stdout, we only filter the task names
# We should find a better way to do so.
if 'TASK [ovirt-ansible-disaster-recovery : ' in line:
sys.stdout.write("\n" + line + "\n")
f.write(line)

print("\n%s%sFinished cleanup of setup '%s'"
" for oVirt ansible disaster recovery%s"
% (INFO,
PREFIX,
source_map,
END))

print("\n%s%sStarting fail-back process to setup '%s'"
" from setup '%s' for oVirt ansible disaster recovery"
% (INFO,
PREFIX,
target_host,
source_map))

f.write("Executing command %s" % ' '.join(map(str, cmd_fb)))
proc_fb = subprocess.Popen(cmd_fb,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
for line in iter(proc_fb.stdout.readline, ''):
# TODO: since we dont want to have log and print the
# progress in the stdout, we only filter the task names
# We should find a better way to do so.
if 'TASK [ovirt-ansible-disaster-recovery :' in line:
sys.stdout.write("\n" + line + "\n")
if "[Failback Replication Sync]" in line:
sys.stdout.write("\n" + INPUT + line + END)
f.write(line)

call(["cat", "report.log"])
print("\n%s%sFinished failback operation"
" for oVirt ansible disaster recovery%s"
% (INFO,
PREFIX,
END))

def _init_vars(self, conf_file):
""" Declare constants """
_SECTION = "failover_failback"
_TARGET = "dr_target_host"
_SOURCE = "dr_source_map"
_VAULT = "vault"
_VAR_FILE = "var_file"
_ANSIBLE_PLAY = 'ansible_play'
setups = ['primary', 'secondary']

""" Declare varialbles """
target_host, source_map, vault, var_file, ansible_play = \
'', '', '', '', ''
settings = configparser.ConfigParser()
settings._interpolation = configparser.ExtendedInterpolation()
settings.read(conf_file)
if _SECTION not in settings.sections():
settings.add_section(_SECTION)
if not settings.has_option(_SECTION, _TARGET):
settings.set(_SECTION, _TARGET, '')
if not settings.has_option(_SECTION, _SOURCE):
settings.set(_SECTION, _SOURCE, '')
if not settings.has_option(_SECTION, _VAULT):
settings.set(_SECTION, _VAULT, '')
if not settings.has_option(_SECTION, _VAR_FILE):
settings.set(_SECTION, _VAR_FILE, '')
if not settings.has_option(_SECTION, _ANSIBLE_PLAY):
settings.set(_SECTION, _ANSIBLE_PLAY, '')
# We fetch the source map as target host since in failback
# we do the reverse operation.
target_host = settings.get(_SECTION, _SOURCE,
vars=DefaultOption(settings,
_SECTION,
source_map=None))
# We fetch the target host as target the source mapping for failback
# since we do the reverse operation.
source_map = settings.get(_SECTION, _TARGET,
vars=DefaultOption(settings,
_SECTION,
target_host=None))
vault = settings.get(_SECTION, _VAULT,
vars=DefaultOption(settings,
_SECTION,
vault=None))
var_file = settings.get(_SECTION, _VAR_FILE,
vars=DefaultOption(settings,
_SECTION,
var_file=None))
ansible_play = settings.get(_SECTION, _ANSIBLE_PLAY,
vars=DefaultOption(settings,
_SECTION,
ansible_play=None))
while target_host not in setups:
target_host = raw_input(
INPUT + PREFIX + "The target setup was not defined. "
"Please provide the setup which it is failback to "
"(primary or secondary): " + END)
while source_map not in setups:
source_map = raw_input(
INPUT + PREFIX + "The source mapping was not defined. "
"Please provide the source mapping "
"(primary or secondary): " + END)
while not os.path.isfile(var_file):
var_file = raw_input("%s%svar file mapping '%s' does not exist. "
"Please provide a valid mapping var file: %s"
% (INPUT,
PREFIX,
var_file,
END))
while not os.path.isfile(vault):
vault = raw_input("%s%spassword file '%s' does not exist."
"Please provide a valid password file:%s "
% (INPUT,
PREFIX,
vault,
END))
while (not ansible_play) or (not os.path.isfile(ansible_play)):
ansible_play = raw_input("%s%sansible play '%s' "
"is not initialized. "
"Please provide the ansible play file "
"to generate the mapping var file "
"with ('%s'):%s "
% (INPUT,
PREFIX,
str(ansible_play),
PLAY_DEF,
END) or PLAY_DEF)
return (target_host, source_map, var_file, vault, ansible_play)


class DefaultOption(dict):

def __init__(self, config, section, **kv):
self._config = config
self._section = section
dict.__init__(self, **kv)

def items(self):
_items = []
for option in self:
if not self._config.has_option(self._section, option):
_items.append((option, self[option]))
else:
value_in_config = self._config.get(self._section, option)
_items.append((option, value_in_config))
return _items


if __name__ == "__main__":
FailBack().run('dr.conf', '/var/log/ovirt-dr/ovirt-dr.log')