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

Commit

Permalink
Merge pull request #43 from oVirt/makefile_additions
Browse files Browse the repository at this point in the history
Support log for ansible-disaster-recovery + additional fixes
  • Loading branch information
maorlipchuk authored May 17, 2018
2 parents f3e4878 + 4222c49 commit d4b6fc3
Show file tree
Hide file tree
Showing 11 changed files with 484 additions and 249 deletions.
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,15 @@ There are four actions which the user can execute:
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
Each of those actions are using a configuration file which its default location is oVirt.disaster-recovery/files/dr.conf
The configuration file location can be changed using --conf-file flag in the ovirt-dr script.
Log file and log level can be configured as well through the ovirt-dr script using the flags --log-file and --log-level
Example Script
--------------
For mapping file generation:
./ovirt-dr generate
./ovirt-dr generate --log-file=ovirt-dr.log --log-level=DEBUG
For mapping file validation:
./ovirt-dr validate
For fail-over operation:
Expand Down
54 changes: 54 additions & 0 deletions callback_plugins/stdout.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Make coding more python3-ish, this is required for contributions to Ansible
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type

# not only visible to ansible-doc, it also 'declares' the options the plugin requires and how to configure them.
DOCUMENTATION = '''
callback: stdout
callback_type: aggregate
short_description: Output the log of ansible
version_added: "2.0"
description:
- This callback output the log of ansible play tasks.
'''
from datetime import datetime

from ansible.plugins.callback import CallbackBase


class CallbackModule(CallbackBase):
"""
This callback module output the inforamtion with a specific style.
"""
CALLBACK_VERSION = 2.0
CALLBACK_TYPE = 'aggregate'
CALLBACK_NAME = 'stdout'

# only needed if you ship it and don't want to enable by default
CALLBACK_NEEDS_WHITELIST = False

def __init__(self):

# make sure the expected objects are present, calling the base's __init__
super(CallbackModule, self).__init__()

def runner_on_failed(self, host, res, ignore_errors=False):
self._display.display('FAILED: %s %s' % (host, res))

def runner_on_ok(self, host, res):
self._display.display('OK: %s %s' % (host, res))

def runner_on_skipped(self, host, item=None):
self._display.display('SKIPPED: %s' % host)

def runner_on_unreachable(self, host, res):
self._display.display('UNREACHABLE: %s %s' % (host, res))

def runner_on_async_failed(self, host, res, jid):
self._display.display('ASYNC_FAILED: %s %s %s' % (host, res, jid))

def playbook_on_import_for_host(self, host, imported_file):
self._display.display('IMPORTED: %s %s' % (host, imported_file))

def playbook_on_not_import_for_host(self, host, missing_file):
self._display.display('NOTIMPORTED: %s %s' % (host, missing_file))
12 changes: 8 additions & 4 deletions files/dr.conf
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
[log]
log_file=/tmp/ovirt-dr-{}.log
log_level=DEBUG

[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
ansible_play=/usr/share/doc/ovirt-ansible-disaster-recovery/examples/dr_play.yml

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

[failover_failback]
dr_target_host=secondary
dr_source_map=primary
vault=passwords.yml
vault=/usr/share/doc/ovirt-ansible-disaster-recovery/examples/ovirt_passwords.yml
var_file=/var/lib/ovirt-ansible-disaster-recovery/mapping_vars.yml
ansible_play=../examples/dr_play.yml
ansible_play=/usr/share/doc/ovirt-ansible-disaster-recovery/examples/dr_play.yml
182 changes: 117 additions & 65 deletions files/fail_back.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
#!/usr/bin/python
from bcolors import bcolors
from ConfigParser import SafeConfigParser
import logging
import os.path
import shlex
import subprocess
import sys
import time

from subprocess import call

Expand All @@ -15,36 +17,31 @@
END = bcolors.ENDC
PREFIX = "[Failback] "
PLAY_DEF = "../examples/dr_play.yml"
report_name = "report-{}.log"


class FailBack():

def run(self, conf_file, log_file):
print("\n%s%sStart failback operation...%s"
% (INFO,
PREFIX,
END))
def run(self, conf_file, log_file, log_level):
log = self._set_log(log_file, log_level)
log.info("Start failback operation...")
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))
report = report_name.format(int(round(time.time() * 1000)))
log.info("\ntarget_host: %s \n"
"source_map: %s \n"
"var_file: %s \n"
"vault: %s \n"
"ansible_play: %s \n"
"report log: /tmp/%s \n"
% (target_host,
source_map,
var_file,
vault,
ansible_play,
report))

cmd = []
cmd.append("ansible-playbook")
Expand Down Expand Up @@ -72,7 +69,8 @@ def run(self, conf_file, log_file):
cmd_fb.append("@" + vault)
cmd_fb.append("-e")
cmd_fb.append(" dr_target_host=" + target_host +
" dr_source_map=" + source_map)
" dr_source_map=" + source_map +
" dr_report_file=" + report)
cmd_fb.append("--vault-password-file")
cmd_fb.append("vault_secret.sh")
cmd_fb.append("-vvv")
Expand All @@ -81,60 +79,97 @@ def run(self, conf_file, log_file):
vault_pass = raw_input(
INPUT + PREFIX + "Please enter the vault password: " + END)
os.system("export vault_password=\"" + vault_pass + "\"")
log.info("Starting cleanup process of setup %s"
" for oVirt ansible disaster recovery" % target_host)
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)
log.info("Executing cleanup command: %s" % ' '.join(map(str, cmd)))
if log_file is not None and log_file != '':
self._log_to_file(log_file, cmd)
else:
self._log_to_console(cmd, log)

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)
log.info("Finished cleanup of setup %s"
" for oVirt ansible disaster recovery" % source_map)
print("\n%s%sFinished cleanup of setup '%s'"
" for oVirt ansible disaster recovery%s"
% (INFO,
PREFIX,
source_map,
END))

log.info("Start failback DR from setup '%s'" % target_host)
print("\n%s%sStarting fail-back process to setup '%s'"
" from setup '%s' for oVirt ansible disaster recovery%s"
% (INFO,
PREFIX,
target_host,
source_map,
END))

log.info("Executing failback command: %s"
% ' '.join(map(str, cmd_fb)))
if log_file is not None and log_file != '':
self._log_to_file(log_file, cmd_fb)
else:
self._log_to_console(cmd_fb, log)

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

def _log_to_file(self, log_file, cmd):
with open(log_file, "a") as f:
proc = subprocess.Popen(cmd,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
for line in iter(proc.stdout.readline, ''):
if 'TASK [' in line:
print("\n%s%s%s\n" % (INFO,
line,
END))
if "[Failback Replication Sync]" in line:
print("%s%s%s" % (INFO, line, END))
f.write(line)
for line in iter(proc.stderr.readline, ''):
f.write(line)
print("%s%s%s" % (WARN,
line,
END))

def _log_to_console(self, cmd, log):
proc = subprocess.Popen(cmd,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
for line in iter(proc.stdout.readline, ''):
if "[Failback Replication Sync]" in line:
print("%s%s%s" % (INFO, line, END))
else:
log.debug(line)
for line in iter(proc.stderr.readline, ''):
log.warn(line)
self._handle_result(subprocess, cmd)

def _handle_result(self, subprocess, cmd):
try:
proc = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
# do something with output
except subprocess.CalledProcessError, e:
print("%sException: %s\n\n"
"failback operation failed, please check log file for "
"further details.%s"
% (FAIL,
e,
END))
exit()

def _init_vars(self, conf_file):
""" Declare constants """
_SECTION = "failover_failback"
Expand Down Expand Up @@ -205,7 +240,7 @@ def _init_vars(self, conf_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 "
" Please provide a valid password file:%s "
% (INPUT,
PREFIX,
vault,
Expand All @@ -223,6 +258,20 @@ def _init_vars(self, conf_file):
END) or PLAY_DEF)
return (target_host, source_map, var_file, vault, ansible_play)

def _set_log(self, log_file, log_level):
logger = logging.getLogger(PREFIX)
formatter = logging.Formatter(
'%(asctime)s %(levelname)s %(message)s')
if log_file is not None and log_file != '':
hdlr = logging.FileHandler(log_file)
hdlr.setFormatter(formatter)
logger.addHandler(hdlr)
else:
ch = logging.StreamHandler(sys.stdout)
logger.addHandler(ch)
logger.setLevel(log_level)
return logger


class DefaultOption(dict):

Expand All @@ -243,4 +292,7 @@ def items(self):


if __name__ == "__main__":
FailBack().run('dr.conf', '/var/log/ovirt-dr/ovirt-dr.log')
level = logging.getLevelName("DEBUG")
conf = 'dr.conf'
log = '/tmp/ovirt-dr.log'
FailBack().run(conf, log, level)
Loading

0 comments on commit d4b6fc3

Please sign in to comment.