freezer-web-ui/disaster_recovery/utils.py

223 lines
7.2 KiB
Python

# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import logging
import datetime
import re
from functools import wraps
from django.core.urlresolvers import reverse
from django.template.defaultfilters import date as django_date
from django.utils.translation import ugettext_lazy as _
from horizon import exceptions
from horizon import get_user_home
from oslo_utils import uuidutils
LOG = logging.getLogger(__name__)
def create_dict(**kwargs):
"""Create a dict only with values that exists so we avoid send keys with
None values
"""
return {k: v for k, v in kwargs.items() if v}
def timestamp_to_string(ts):
return django_date(
datetime.datetime.fromtimestamp(int(ts)),
'SHORT_DATETIME_FORMAT')
def create_dummy_id():
"""Generate a dummy id for documents generated by the scheduler.
This is needed when the scheduler creates jobs with actions attached
directly, those actions are not registered in the db.
"""
return uuidutils.generate_uuid(dashed=False)
def get_action_ids(ids):
"""Return an ordered list of actions for a new job
"""
ids = ids.split('===')
return [i for i in ids if i]
def assign_and_remove(source_dict, dest_dict, key):
"""Assign a value to a destination dict from a source dict
if the key exists
"""
if key in source_dict:
dest_dict[key] = source_dict.pop(key)
class SessionObject(object):
def __init__(self, session_id, description, status, jobs,
start_datetime, interval, end_datetime):
self.session_id = session_id
self.id = session_id
self.description = description
self.status = status
self.jobs = jobs or []
self.schedule_start_date = start_datetime
self.schedule_end_date = end_datetime
self.schedule_interval = interval
class JobObject(object):
def __init__(self, job_id, description, result, event, client_id='_'):
self.job_id = job_id
self.id = job_id
self.description = description
self._result = result
self._event = event
# Checking if client_id composed like <tenant_id>_<hostname>
if re.search("^[a-z0-9]{32}_.+", client_id):
self.client_id = client_id.split('_')[1]
else:
self.client_id = client_id
@property
def event(self):
return self._event or 'stop'
@property
def result(self):
return self._result or 'pending'
class JobsInSessionObject(object):
def __init__(self, job_id, session_id, client_id, result):
self.job_id = job_id
self.session_id = session_id
self.id = session_id
self.client_id = client_id
self.result = result or 'pending'
class ActionObject(object):
def __init__(self, action_id=None, action=None, backup_name=None,
job_id=None):
# action basic info
self.id = action_id
self.action_id = action_id or create_dummy_id()
self.action = action or 'backup'
self.backup_name = backup_name or 'no backup name available'
self.job_id = job_id
class ActionObjectDetail(object):
def __init__(self, action_id=None, action=None, backup_name=None,
path_to_backup=None, storage=None, mode=None, container=None,
mandatory=None, max_retries=None, max_retries_interval=None):
# action basic info
self.id = action_id
self.action_id = action_id or create_dummy_id()
self.action = action or 'backup'
self.backup_name = backup_name or 'no backup name available'
self.path_to_backup = path_to_backup
self.storage = storage or 'swift'
self.mode = mode or 'fs'
self.container = container
# action rules
self.mandatory = mandatory
self.max_retries = max_retries
self.max_retries_interval = max_retries_interval
class BackupObject(object):
def __init__(self, backup_id=None, action=None, time_stamp=None,
backup_name=None, backup_media=None, path_to_backup=None,
hostname=None, level=None, container=None,
curr_backup_level=None, encrypted=None,
total_broken_links=None, excluded_files=None, storage=None,
ssh_host=None, ssh_key=None, ssh_username=None,
ssh_port=None, mode=None):
self.backup_id = backup_id
self.id = backup_id
self.backup_name = backup_name
self.action = action or 'backup'
self.time_stamp = time_stamp
self.backup_media = backup_media or 'fs'
self.path_to_backup = path_to_backup
self.hostname = hostname
self.container = container
self.level = level
self.curr_backup_level = curr_backup_level or 0
self.encrypted = encrypted
self.total_broken_links = total_broken_links or 0
self.excluded_files = excluded_files
self.storage = storage
self.ssh_host = ssh_host
self.ssh_key = ssh_key
self.ssh_username = ssh_username
self.ssh_port = ssh_port or 22
self.mode = mode or 'fs'
class ClientObject(object):
def __init__(self, hostname, client_id, client_uuid):
self.hostname = hostname
self.client_id = client_id
self.uuid = client_uuid
self.id = client_id
def shield(message, redirect=''):
"""decorator to reduce boilerplate try except blocks for horizon functions
:param message: a str error message
:param redirect: a str with the redirect namespace without including
horizon:disaster_recovery:
eg. @shield('error', redirect='jobs:index')
"""
def wrap(function):
@wraps(function)
def wrapped_function(view, *args, **kwargs):
try:
return function(view, *args, **kwargs)
except Exception as error:
LOG.error(error.message)
namespace = "horizon:disaster_recovery:"
r = reverse("{0}{1}".format(namespace, redirect))
if view.request.path == r:
# To avoid an endless loop, we must not redirect to the
# same page on which the error happened
user_home = get_user_home(view.request.user)
exceptions.handle(view.request, _(error.message),
redirect=user_home)
else:
exceptions.handle(view.request, _(error.message),
redirect=r)
return wrapped_function
return wrap
def timestamp_to_iso(ts):
"""Generate an iso date from time stamp
:param ts: time stamp
:return: iso date
"""
return datetime.datetime.fromtimestamp(ts).isoformat()