Horizon implementation for jobs api endpoint
Implements: blueprint freezer-api-web-ui Change-Id: I8339d4b319f85964d33a2ab5d5c5e3669ca55f1c
This commit is contained in:
parent
a7470fd5dc
commit
206a0ec9b7
|
@ -1 +0,0 @@
|
|||
__author__ = 'jonas'
|
|
@ -1,73 +0,0 @@
|
|||
# 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.
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from horizon import tables
|
||||
from horizon.utils import functions as utils
|
||||
|
||||
from horizon_web_ui.freezer_ui.django_utils import timestamp_to_string
|
||||
|
||||
|
||||
class ActionsTable(tables.DataTable):
|
||||
METADATA_LOADED_CHOICES = (
|
||||
(False, None),
|
||||
(True, True),
|
||||
)
|
||||
|
||||
STATUS_DISPLAY = (
|
||||
('pending', 'Pending'),
|
||||
('started', 'Started'),
|
||||
('abort_req', 'Abort Requested'),
|
||||
('aborting', 'Aborting'),
|
||||
('aborted', 'Aborted'),
|
||||
('success', 'Success'),
|
||||
('fail', 'Failed')
|
||||
)
|
||||
|
||||
TYPE_DISPLAY = (
|
||||
('restore', 'Restore'),
|
||||
('backup', 'Backup (Unscheduled)')
|
||||
)
|
||||
|
||||
client_id = tables.Column("client_id", verbose_name=_("Client Id"))
|
||||
type = tables.Column('action', verbose_name=_("Type"),
|
||||
display_choices=TYPE_DISPLAY)
|
||||
description = tables.Column("description", verbose_name=_("Description"))
|
||||
status = tables.Column('status',
|
||||
verbose_name=_("Status"),
|
||||
display_choices=STATUS_DISPLAY)
|
||||
created = tables.Column('time_created', verbose_name=_("Created"),
|
||||
filters=(timestamp_to_string,))
|
||||
started = tables.Column('time_started', verbose_name=_("Started"),
|
||||
filters=(timestamp_to_string,))
|
||||
ended = tables.Column('time_ended', verbose_name=_("Ended"),
|
||||
filters=(timestamp_to_string,))
|
||||
|
||||
def get_object_id(self, action):
|
||||
return action.id
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(ActionsTable, self).__init__(*args, **kwargs)
|
||||
|
||||
if 'offset' in self.request.GET:
|
||||
self.offset = self.request.GET['offset']
|
||||
else:
|
||||
self.offset = 0
|
||||
|
||||
def get_pagination_string(self):
|
||||
page_size = utils.get_page_size(self.request)
|
||||
return "=".join(['offset', str(self.offset + page_size)])
|
||||
|
||||
class Meta(object):
|
||||
name = "jobs"
|
||||
verbose_name = _("Jobs")
|
|
@ -1,22 +0,0 @@
|
|||
# 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.
|
||||
|
||||
from django.conf.urls import patterns
|
||||
from django.conf.urls import url
|
||||
|
||||
from horizon_web_ui.freezer_ui.actions import views
|
||||
|
||||
|
||||
urlpatterns = patterns(
|
||||
'',
|
||||
url(r'^$', views.IndexView.as_view(), name='index'),
|
||||
)
|
|
@ -1,34 +0,0 @@
|
|||
# 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.
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from horizon import tables
|
||||
from horizon_web_ui.freezer_ui.actions import tables as actions_tables
|
||||
from horizon_web_ui.freezer_ui.api import api as freezer_api
|
||||
|
||||
|
||||
class IndexView(tables.DataTableView):
|
||||
name = _("Jobs")
|
||||
slug = "actions"
|
||||
table_class = actions_tables.ActionsTable
|
||||
template_name = ("freezer_ui/actions/index.html")
|
||||
|
||||
def has_more_data(self, table):
|
||||
return self._has_more
|
||||
|
||||
def get_data(self):
|
||||
backups, self._has_more = freezer_api.actions_list(
|
||||
self.request,
|
||||
offset=self.table.offset)
|
||||
|
||||
return backups
|
|
@ -17,8 +17,8 @@
|
|||
from django.conf import settings
|
||||
import warnings
|
||||
import freezer.apiclient.client
|
||||
from horizon_web_ui.freezer_ui.utils import create_dict_action
|
||||
|
||||
from horizon.utils import functions as utils
|
||||
from horizon.utils.memoized import memoized # noqa
|
||||
|
||||
|
||||
|
@ -51,19 +51,19 @@ class Dict2Object(object):
|
|||
|
||||
|
||||
class Action(Dict2Object):
|
||||
nested_dict = 'job'
|
||||
nested_dict = 'job_action'
|
||||
|
||||
@property
|
||||
def id(self):
|
||||
return self.action_id
|
||||
return self.job_id
|
||||
|
||||
|
||||
class Configuration(Dict2Object):
|
||||
nested_dict = 'config_file'
|
||||
class Job(Dict2Object):
|
||||
nested_dict = 'job_actions'
|
||||
|
||||
@property
|
||||
def id(self):
|
||||
return self.config_id
|
||||
return self.job_id
|
||||
|
||||
|
||||
class Backup(Dict2Object):
|
||||
|
@ -81,17 +81,13 @@ class Client(Dict2Object):
|
|||
def id(self):
|
||||
return self.client_id
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self.client_id
|
||||
|
||||
|
||||
class ConfigClient(object):
|
||||
|
||||
def __init__(self, name, last_backup):
|
||||
self.id = name
|
||||
self.name = name
|
||||
self.last_backup = last_backup
|
||||
class ActionJob(object):
|
||||
def __init__(self, job_id, action_id, action, backup_name):
|
||||
self.job_id = job_id
|
||||
self.action_id = action_id
|
||||
self.action = action
|
||||
self.backup_name = backup_name
|
||||
|
||||
|
||||
@memoized
|
||||
|
@ -125,188 +121,145 @@ def _freezerclient(request):
|
|||
endpoint=api_url)
|
||||
|
||||
|
||||
def configuration_create(request, name=None, container_name=None,
|
||||
src_file=None, levels=None, optimize=None,
|
||||
compression=None, encryption_password=None,
|
||||
clients=[], start_datetime=None, interval=None,
|
||||
exclude=None, log_file=None, proxy=None,
|
||||
max_priority=False):
|
||||
"""Create a new configuration file """
|
||||
|
||||
data = {
|
||||
"name": name,
|
||||
"container_name": container_name,
|
||||
"src_file": src_file,
|
||||
"levels": levels,
|
||||
"optimize": optimize,
|
||||
"compression": compression,
|
||||
"encryption_password": encryption_password,
|
||||
"clients": clients,
|
||||
"start_datetime": start_datetime,
|
||||
"interval": interval,
|
||||
"exclude": exclude,
|
||||
"log_file": log_file,
|
||||
"proxy": proxy,
|
||||
"max_priority": max_priority
|
||||
def job_create(request, context):
|
||||
"""Create a new job file """
|
||||
job = create_dict_action(**context)
|
||||
job['description'] = job.pop('description', None)
|
||||
job['client_id'] = job.pop('client_id', None)
|
||||
schedule = {
|
||||
'end_datetime': job.pop('end_datetime', None),
|
||||
'interval': job.pop('interval', None),
|
||||
'start_datetime': job.pop('start_datetime', None),
|
||||
}
|
||||
return _freezerclient(request).configs.create(data)
|
||||
job['job_schedule'] = schedule
|
||||
job['job_actions'] = []
|
||||
return _freezerclient(request).jobs.create(job)
|
||||
|
||||
|
||||
def configuration_update(request, config_id=None, name=None,
|
||||
src_file=None, levels=None, optimize=None,
|
||||
compression=None, encryption_password=None,
|
||||
clients=[], start_datetime=None, interval=None,
|
||||
exclude=None, log_file=None, proxy=None,
|
||||
max_priority=False, container_name=None,):
|
||||
|
||||
"""Update a new configuration file """
|
||||
data = {
|
||||
"name": name,
|
||||
"container_name": container_name,
|
||||
"src_file": src_file,
|
||||
"levels": levels,
|
||||
"optimize": optimize,
|
||||
"compression": compression,
|
||||
"encryption_password": encryption_password,
|
||||
"clients": clients,
|
||||
"start_datetime": start_datetime,
|
||||
"interval": interval,
|
||||
"exclude": exclude,
|
||||
"log_file": log_file,
|
||||
"proxy": proxy,
|
||||
"max_priority": max_priority
|
||||
def job_edit(request, context):
|
||||
"""Edit an existing job file, but leave the actions to actions_edit"""
|
||||
job = create_dict_action(**context)
|
||||
job['description'] = job.pop('description', None)
|
||||
job['client_id'] = job.pop('client_id', None)
|
||||
schedule = {
|
||||
'end_datetime': job.pop('end_datetime', None),
|
||||
'interval': job.pop('interval', None),
|
||||
'start_datetime': job.pop('start_datetime', None),
|
||||
}
|
||||
return _freezerclient(request).configs.update(config_id, data)
|
||||
job['job_schedule'] = schedule
|
||||
job_id = job.pop('original_name', None)
|
||||
return _freezerclient(request).jobs.update(job_id, job)
|
||||
|
||||
|
||||
def configuration_delete(request, obj_id):
|
||||
return _freezerclient(request).configs.delete(obj_id)
|
||||
def job_delete(request, obj_id):
|
||||
return _freezerclient(request).jobs.delete(obj_id)
|
||||
|
||||
|
||||
def configuration_clone(request, config_id):
|
||||
config_file = _freezerclient(request).configs.get(config_id)
|
||||
data = config_file[0]['config_file']
|
||||
data['name'] = '{0}_clone'.format(data['name'])
|
||||
return _freezerclient(request).configs.create(data)
|
||||
def job_clone(request, job_id):
|
||||
job_file = _freezerclient(request).jobs.get(job_id)
|
||||
job_file['description'] = \
|
||||
'{0}_clone'.format(job_file['description'])
|
||||
job_file.pop('job_id', None)
|
||||
job_file.pop('_version', None)
|
||||
return _freezerclient(request).jobs.create(job_file)
|
||||
|
||||
|
||||
def configuration_get(request, config_id):
|
||||
config_file = _freezerclient(request).configs.get(config_id)
|
||||
if config_file:
|
||||
return [Configuration(data) for data in config_file]
|
||||
def job_get(request, job_id):
|
||||
job_file = _freezerclient(request).jobs.get(job_id)
|
||||
if job_file:
|
||||
job_item = [job_file]
|
||||
job = [Job(data) for data in job_item]
|
||||
return job
|
||||
return []
|
||||
|
||||
|
||||
def configuration_list(request):
|
||||
configurations = _freezerclient(request).configs.list()
|
||||
configurations = [Configuration(data) for data in configurations]
|
||||
return configurations
|
||||
def job_list(request):
|
||||
jobs = _freezerclient(request).jobs.list_all()
|
||||
jobs = [Job(data) for data in jobs]
|
||||
return jobs
|
||||
|
||||
|
||||
def clients_in_config(request, config_id):
|
||||
configuration = configuration_get(request, config_id)
|
||||
clients = []
|
||||
last_backup = None
|
||||
clients_dict = [c.get_dict() for c in configuration]
|
||||
for client in clients_dict:
|
||||
for client_id in client['config_file']['clients']:
|
||||
backups, has_more = backups_list(request, text_match=client_id)
|
||||
backups = [Backup(data) for data in backups]
|
||||
backups = [b.get_dict() for b in backups]
|
||||
for backup in backups:
|
||||
last_backup = backup.data_dict['backup_metadata']['timestamp']
|
||||
clients.append(ConfigClient(client_id, last_backup))
|
||||
return clients
|
||||
def action_create(request, context):
|
||||
"""Create a new action for a job """
|
||||
action = {}
|
||||
|
||||
if context['max_retries']:
|
||||
action['max_retries'] = context.pop('max_retries')
|
||||
if context['max_retries_interval']:
|
||||
action['max_retries_interval'] = context.pop('max_retries_interval')
|
||||
if context['mandatory']:
|
||||
action['mandatory'] = context.pop('mandatory')
|
||||
|
||||
job_id = context.pop('original_name')
|
||||
job_action = create_dict_action(**context)
|
||||
action['freezer_action'] = job_action
|
||||
action_id = _freezerclient(request).actions.create(action)
|
||||
action['action_id'] = action_id
|
||||
job = _freezerclient(request).jobs.get(job_id)
|
||||
job['job_actions'].append(action)
|
||||
return _freezerclient(request).jobs.update(job_id, job)
|
||||
|
||||
|
||||
def client_list(request, limit=20):
|
||||
clients = _freezerclient(request).registration.list(limit=limit)
|
||||
def action_list(request):
|
||||
actions = _freezerclient(request).actions.list()
|
||||
actions = [Action(data) for data in actions]
|
||||
return actions
|
||||
|
||||
|
||||
def actions_in_job(request, job_id):
|
||||
job = _freezerclient(request).jobs.get(job_id)
|
||||
actions = []
|
||||
try:
|
||||
job_id = job['job_id']
|
||||
actions = [ActionJob(job_id,
|
||||
action['action_id'],
|
||||
action['freezer_action']['action'],
|
||||
action['freezer_action']['backup_name'])
|
||||
for action in job['job_actions']]
|
||||
except Exception:
|
||||
warnings.warn('No more actions in your job')
|
||||
return actions
|
||||
|
||||
|
||||
def action_get(request, action_id):
|
||||
action = _freezerclient(request).actions.get(action_id)
|
||||
return action
|
||||
|
||||
|
||||
def action_update(request, context):
|
||||
job_id = context.pop('original_name')
|
||||
action_id = context.pop('action_id')
|
||||
|
||||
job = _freezerclient(request).jobs.get(job_id)
|
||||
|
||||
for a in job['job_actions']:
|
||||
if a['action_id'] == action_id:
|
||||
|
||||
if context['max_retries']:
|
||||
a['max_retries'] = context.pop('max_retries')
|
||||
if context['max_retries_interval']:
|
||||
a['max_retries_interval'] = \
|
||||
context.pop('max_retries_interval')
|
||||
if context['mandatory']:
|
||||
a['mandatory'] = context.pop('mandatory')
|
||||
|
||||
updated_action = create_dict_action(**context)
|
||||
|
||||
a['freezer_action'].update(updated_action)
|
||||
|
||||
return _freezerclient(request).jobs.update(job_id, job)
|
||||
|
||||
|
||||
def action_delete(request, ids):
|
||||
action_id, job_id = ids.split('===')
|
||||
job = _freezerclient(request).jobs.get(job_id)
|
||||
for action in job['job_actions']:
|
||||
if action['action_id'] == action_id:
|
||||
job['job_actions'].remove(action)
|
||||
return _freezerclient(request).jobs.update(job_id, job)
|
||||
|
||||
|
||||
def client_list(request):
|
||||
clients = _freezerclient(request).registration.list()
|
||||
clients = [Client(client) for client in clients]
|
||||
return clients
|
||||
|
||||
|
||||
def backups_list(request, offset=0, time_after=None, time_before=None,
|
||||
text_match=None):
|
||||
page_size = utils.get_page_size(request)
|
||||
|
||||
search = {}
|
||||
|
||||
if time_after:
|
||||
search['time_after'] = time_after
|
||||
if time_before:
|
||||
search['time_before'] = time_before
|
||||
|
||||
if text_match:
|
||||
search['match'] = [
|
||||
{
|
||||
"_all": text_match,
|
||||
}
|
||||
]
|
||||
|
||||
backups = _freezerclient(request).backups.list(
|
||||
limit=page_size + 1,
|
||||
offset=offset,
|
||||
search=search)
|
||||
|
||||
if len(backups) > page_size:
|
||||
backups.pop()
|
||||
has_more = True
|
||||
else:
|
||||
has_more = False
|
||||
|
||||
# Wrap data in object for easier handling
|
||||
backups = [Backup(data) for data in backups]
|
||||
|
||||
return backups, has_more
|
||||
|
||||
|
||||
def backup_get(request, backup_id):
|
||||
data = _freezerclient(request).backups.get(backup_id)
|
||||
if data:
|
||||
return Backup(data[0])
|
||||
|
||||
|
||||
def restore_action_create(request,
|
||||
backup_id,
|
||||
destination_client_id,
|
||||
destination_path,
|
||||
description=None,
|
||||
dry_run=False,
|
||||
max_prio=False):
|
||||
c = _freezerclient(request)
|
||||
backup = c.backups.get(backup_id)[0]
|
||||
|
||||
action = {
|
||||
"job": {
|
||||
"action": "restore",
|
||||
"container_name": backup['backup_metadata']['container'],
|
||||
"restore-abs-path": destination_path,
|
||||
"backup-name": backup['backup_metadata']['backup_name'],
|
||||
"restore-from-host": backup['backup_metadata']['host_name'],
|
||||
"max_cpu_priority": max_prio,
|
||||
"dry_run": dry_run
|
||||
},
|
||||
"description": description,
|
||||
"client_id": destination_client_id
|
||||
}
|
||||
|
||||
c.actions.create(action)
|
||||
|
||||
|
||||
def actions_list(request, offset=0):
|
||||
page_size = utils.get_page_size(request)
|
||||
|
||||
actions = _freezerclient(request).actions.list(
|
||||
limit=page_size + 1,
|
||||
offset=offset)
|
||||
|
||||
if len(actions) > page_size:
|
||||
actions.pop()
|
||||
has_more = True
|
||||
else:
|
||||
has_more = False
|
||||
|
||||
# Wrap data in object for easier handling
|
||||
actions = [Action(data['action']) for data in actions]
|
||||
|
||||
return actions, has_more
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
"""
|
||||
Stub file to work around django bug: https://code.djangoproject.com/ticket/7198
|
||||
"""
|
|
@ -1,107 +0,0 @@
|
|||
# Copyright 2012 United States Government as represented by the
|
||||
# Administrator of the National Aeronautics and Space Administration.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Copyright 2012 Nebula, Inc.
|
||||
#
|
||||
# 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
|
||||
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from horizon import forms
|
||||
from horizon import workflows
|
||||
|
||||
import horizon_web_ui.freezer_ui.api.api as freezer_api
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class DestinationAction(workflows.MembershipAction):
|
||||
path = forms.CharField(label=_("Destination Path"),
|
||||
initial='/home/',
|
||||
help_text="The path in which the backup should be "
|
||||
"restored",
|
||||
required=True)
|
||||
backup_id = forms.CharField(widget=forms.HiddenInput())
|
||||
|
||||
def clean(self):
|
||||
if 'client' in self.request.POST:
|
||||
self.cleaned_data['client'] = self.request.POST['client']
|
||||
else:
|
||||
raise ValidationError('Client is required')
|
||||
|
||||
return self.cleaned_data
|
||||
|
||||
class Meta(object):
|
||||
name = _("Destination")
|
||||
slug = "destination"
|
||||
|
||||
|
||||
class Destination(workflows.Step):
|
||||
template_name = 'freezer_ui/backups/restore.html'
|
||||
action_class = DestinationAction
|
||||
contributes = ('client', 'path', 'backup_id')
|
||||
|
||||
def has_required_fields(self):
|
||||
return True
|
||||
|
||||
|
||||
class OptionsAction(workflows.Action):
|
||||
description = forms.CharField(widget=forms.Textarea,
|
||||
label="Description",
|
||||
required=False,
|
||||
help_text="Free text description of this "
|
||||
"restore.")
|
||||
|
||||
dry_run = forms.BooleanField(label=_("Dry Run"),
|
||||
required=False)
|
||||
max_prio = forms.BooleanField(label=_("Max Process Priority"),
|
||||
required=False)
|
||||
|
||||
class Meta(object):
|
||||
name = _("Options")
|
||||
|
||||
|
||||
class Options(workflows.Step):
|
||||
action_class = OptionsAction
|
||||
contributes = ('description', 'dry_run', 'max_prio')
|
||||
after = Destination
|
||||
|
||||
|
||||
class ConfigureBackups(workflows.Workflow):
|
||||
slug = "restore"
|
||||
name = _("Restore")
|
||||
success_url = "horizon:freezer_ui:backups:index"
|
||||
success_message = "Restore job successfully queued. It will get " \
|
||||
"executed soon."
|
||||
wizard = False
|
||||
default_steps = (Destination, Options)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(ConfigureBackups, self).__init__(*args, **kwargs)
|
||||
pass
|
||||
|
||||
def handle(self, request, data):
|
||||
freezer_api.restore_action_create(
|
||||
request,
|
||||
backup_id=data['backup_id'],
|
||||
destination_client_id=data['client'],
|
||||
destination_path=data['path'],
|
||||
description=data['description'],
|
||||
dry_run=data['dry_run'],
|
||||
max_prio=data['max_prio']
|
||||
)
|
||||
return True
|
|
@ -1,111 +0,0 @@
|
|||
# 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.
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.utils import safestring
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from horizon import tables
|
||||
from horizon.utils import functions as utils
|
||||
from horizon_web_ui.freezer_ui.django_utils import timestamp_to_string
|
||||
|
||||
|
||||
class Restore(tables.LinkAction):
|
||||
name = "restore"
|
||||
verbose_name = _("Restore")
|
||||
classes = ("ajax-modal", "btn-launch")
|
||||
ajax = True
|
||||
|
||||
def get_link_url(self, datum=None):
|
||||
return reverse("horizon:freezer_ui:backups:restore",
|
||||
kwargs={'backup_id': datum.id})
|
||||
|
||||
def allowed(self, request, instance):
|
||||
return True # is_loaded(instance)
|
||||
|
||||
|
||||
class BackupFilter(tables.FilterAction):
|
||||
filter_type = "server"
|
||||
filter_choices = (("before", "Created before", True),
|
||||
("after", "Created after", True),
|
||||
("between", "Created between", True),
|
||||
("contains", "Contains text", True))
|
||||
|
||||
|
||||
def icons(backup):
|
||||
result = []
|
||||
|
||||
placeholder = '<i class="fa fa-fw"></i>'
|
||||
|
||||
level_txt = "Level: {} ({} backup) out of {}".format(
|
||||
backup.level, "Full" if backup.level == 0 else "Incremental",
|
||||
backup.max_level)
|
||||
result.append(
|
||||
'<i class="fa fa-fw fa-custom-number" title="{}">{}</i>'.format(
|
||||
level_txt, backup.level))
|
||||
|
||||
if backup.encrypted:
|
||||
result.append(
|
||||
'<i class="fa fa-lock fa-fw" title="Backup is encrypted"></i>')
|
||||
else:
|
||||
result.append(placeholder)
|
||||
|
||||
if int(backup.total_broken_links) > 0:
|
||||
result.append(
|
||||
'<i class="fa fa-chain-broken fa-fw" title="There are {} broken '
|
||||
'links in this backup"></i>'.format(backup.total_broken_links))
|
||||
else:
|
||||
result.append(placeholder)
|
||||
|
||||
if backup.excluded_files:
|
||||
result.append(
|
||||
'<i class="fa fa-minus-square fa-fw" title="{} files have been exc'
|
||||
'luded from this backup"></i>'.format(len(backup.excluded_files)))
|
||||
else:
|
||||
result.append(placeholder)
|
||||
|
||||
return safestring.mark_safe("".join(result))
|
||||
|
||||
|
||||
def backup_detail_view(backup):
|
||||
return reverse("horizon:freezer_ui:backups:detail", args=[backup.id])
|
||||
|
||||
|
||||
class BackupsTable(tables.DataTable):
|
||||
backup_name = tables.Column('backup_name', verbose_name=_("Backup Name"),
|
||||
link=backup_detail_view)
|
||||
host_name = tables.Column('host_name', verbose_name=_("Host Name"))
|
||||
created_by = tables.Column("user_name", verbose_name=_("Created By"))
|
||||
created = tables.Column("timestamp",
|
||||
verbose_name=_("Created At"),
|
||||
filters=[timestamp_to_string])
|
||||
icons = tables.Column(icons, verbose_name='Info')
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(BackupsTable, self).__init__(*args, **kwargs)
|
||||
|
||||
if 'offset' in self.request.GET:
|
||||
self.offset = self.request.GET['offset']
|
||||
else:
|
||||
self.offset = 0
|
||||
|
||||
def get_object_id(self, backup):
|
||||
return backup.id
|
||||
|
||||
def get_pagination_string(self):
|
||||
page_size = utils.get_page_size(self.request)
|
||||
return "=".join(['offset', str(self.offset + page_size)])
|
||||
|
||||
class Meta(object):
|
||||
name = "vms"
|
||||
verbose_name = _("Backups")
|
||||
row_actions = (Restore,)
|
||||
table_actions = (BackupFilter, )
|
||||
multi_select = False
|
|
@ -1,17 +0,0 @@
|
|||
{% extends 'base.html' %}
|
||||
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Backups" %}{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
{% include "horizon/common/_page_header.html" with title=_("Backups") %}
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block main %}
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<pre>{{ data }}</pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
|
@ -1,17 +0,0 @@
|
|||
{% extends 'base.html' %}
|
||||
|
||||
{% block css %}
|
||||
{% include "_stylesheets.html" %}
|
||||
<link href='{{ STATIC_URL }}freezer/css/freezer.css' type='text/css' media='screen' rel='stylesheet' />
|
||||
{% endblock %}
|
||||
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "VMs" %}{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
{% include "horizon/common/_page_header.html" with title=_("Backups") %}
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block main %}
|
||||
{{ table.render }}
|
||||
{% endblock %}
|
|
@ -1,46 +0,0 @@
|
|||
<noscript><h3>{{ step }}</h3></noscript>
|
||||
<div class="row" ng-controller="DestinationCtrl">
|
||||
<div class="col-sm-12">
|
||||
<table class="table table-bordered table-striped">
|
||||
<thead>
|
||||
<tr class="table_caption">
|
||||
<th class="table_header" colspan="3" data-column="0">
|
||||
<div class="table_actions clearfix">
|
||||
<div class="table_search">
|
||||
<input class="form-control" ng-model="query">
|
||||
</div>
|
||||
</div>
|
||||
</th>
|
||||
</tr>
|
||||
<tr ng-hide="filtered.length <= 0">
|
||||
<th class="multi_select_column"></th>
|
||||
<th>Hostname</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="client in filtered = (clients | filter: { client : { $ : query } } | limitTo:10)">
|
||||
<td class="multi_select_column">
|
||||
<input type="radio" name="client" value="{$ client['client']['client_id'] $}">
|
||||
</td>
|
||||
<td>{$ client['client']['client_id'] $}</td>
|
||||
<td>{$ client['client']['description'] $}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td colspan="3" data-column="0">
|
||||
{$ filtered.length ? filtered.length : 0 $} out of {$ clients.length $} displayed. Use the filter field to limit the number of results.
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
{% include "horizon/common/_form_fields.html" %}
|
||||
{{ table.render }}
|
||||
</div>
|
||||
<div class="col-sm-12">
|
||||
{{ step.get_help_text }}
|
||||
</div>
|
||||
</div>
|
|
@ -1,26 +0,0 @@
|
|||
# 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.
|
||||
|
||||
from django.conf.urls import patterns
|
||||
from django.conf.urls import url
|
||||
|
||||
from horizon_web_ui.freezer_ui.backups import views
|
||||
|
||||
|
||||
urlpatterns = patterns(
|
||||
'',
|
||||
url(r'^$', views.IndexView.as_view(), name='index'),
|
||||
url(r'^(?P<backup_id>[^/]*)$', views.DetailView.as_view(), name='detail'),
|
||||
url(r'^restore/(?P<backup_id>.*)$',
|
||||
views.RestoreView.as_view(),
|
||||
name='restore'),
|
||||
)
|
|
@ -1,141 +0,0 @@
|
|||
# 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 datetime
|
||||
import pprint
|
||||
import time
|
||||
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.template.defaultfilters import date as django_date
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.views import generic
|
||||
import parsedatetime as pdt
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon import messages
|
||||
from horizon import tables
|
||||
from horizon import workflows
|
||||
from horizon_web_ui.freezer_ui.backups import tables as freezer_tables
|
||||
|
||||
import horizon_web_ui.freezer_ui.api.api as freezer_api
|
||||
import restore_workflow
|
||||
|
||||
|
||||
class IndexView(tables.DataTableView):
|
||||
name = _("Backups")
|
||||
slug = "backups"
|
||||
table_class = freezer_tables.BackupsTable
|
||||
template_name = ("freezer_ui/backups/index.html")
|
||||
|
||||
def has_more_data(self, table):
|
||||
return self._has_more
|
||||
|
||||
def get_data(self):
|
||||
filter = self.get_filters(self.request,
|
||||
self.table.get_filter_field(),
|
||||
self.table.get_filter_string())
|
||||
|
||||
backups, self._has_more = freezer_api.backups_list(
|
||||
self.request,
|
||||
offset=self.table.offset,
|
||||
time_after=filter['from'],
|
||||
time_before=filter['to'],
|
||||
text_match=filter['contains']
|
||||
)
|
||||
|
||||
return backups
|
||||
|
||||
def get_filters(self, request, filter_field, filter_string):
|
||||
cal = pdt.Calendar()
|
||||
|
||||
filters = {}
|
||||
filters['from'] = None
|
||||
filters['to'] = None
|
||||
filters['contains'] = None
|
||||
|
||||
if filter_field == 'between':
|
||||
result_range = cal.nlp(filter_string)
|
||||
|
||||
if result_range and len(result_range) == 2:
|
||||
filters['from'] = int(
|
||||
time.mktime(result_range[0][0].timetuple()))
|
||||
filters['to'] = int(
|
||||
time.mktime(result_range[1][0].timetuple()))
|
||||
else:
|
||||
messages.warning(
|
||||
request,
|
||||
"Please enter two dates. E.g: '01/01/2014 - 05/09/2015'.")
|
||||
elif filter_field in ['before', 'after']:
|
||||
result, what = cal.parse(filter_string)
|
||||
|
||||
if what == 0:
|
||||
messages.warning(
|
||||
self.table.request,
|
||||
"Please enter a date/time. E.g: '01/01/2014 12pm' or '1 we"
|
||||
"ek ago'.")
|
||||
else:
|
||||
field = 'to' if filter_field == 'before' else 'from'
|
||||
|
||||
dt = datetime.datetime(*result[:6])
|
||||
|
||||
if what == 1: # a date without time
|
||||
# use .date() to remove time part
|
||||
filters[field] = int(time.mktime(dt.date().timetuple()))
|
||||
elif what in [2, 3]: # date and time or time with current date
|
||||
filters[field] = int(time.mktime(dt.timetuple()))
|
||||
else:
|
||||
raise Exception(
|
||||
'Unknown result when parsing date: {}'.format(what))
|
||||
elif filter_field == 'contains':
|
||||
filters['contains'] = filter_string.lower()
|
||||
|
||||
return filters
|
||||
|
||||
|
||||
class DetailView(generic.TemplateView):
|
||||
template_name = 'freezer_ui/backups/detail.html'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
|
||||
backup = freezer_api.get_backup(self.request, kwargs['backup_id'])
|
||||
return {'data': pprint.pformat(backup.data_dict)}
|
||||
|
||||
|
||||
class RestoreView(workflows.WorkflowView):
|
||||
workflow_class = restore_workflow.ConfigureBackups
|
||||
|
||||
def get_object(self, *args, **kwargs):
|
||||
id = self.kwargs['backup_id']
|
||||
try:
|
||||
return freezer_api.get_backup(self.request, id)
|
||||
except Exception:
|
||||
redirect = reverse("horizon:freezer_ui:backups:index")
|
||||
msg = _('Unable to retrieve details.')
|
||||
exceptions.handle(self.request, msg, redirect=redirect)
|
||||
|
||||
def is_update(self):
|
||||
return 'name' in self.kwargs and bool(self.kwargs['name'])
|
||||
|
||||
def get_workflow_name(self):
|
||||
backup = freezer_api.backup_get(self.request, self.kwargs['backup_id'])
|
||||
backup_date = datetime.datetime.fromtimestamp(int(backup.timestamp))
|
||||
backup_date_str = django_date(backup_date, 'SHORT_DATETIME_FORMAT')
|
||||
return "Restore '{}' from {}".format(
|
||||
backup.backup_name, backup_date_str)
|
||||
|
||||
def get_initial(self):
|
||||
return {"backup_id": self.kwargs['backup_id']}
|
||||
|
||||
def get_workflow(self, *args, **kwargs):
|
||||
workflow = super(RestoreView, self).get_workflow(*args, **kwargs)
|
||||
workflow.name = self.get_workflow_name()
|
||||
|
||||
return workflow
|
|
@ -1 +0,0 @@
|
|||
__author__ = 'jonas'
|
|
@ -1,28 +0,0 @@
|
|||
# Copyright 2012 Nebula, Inc.
|
||||
#
|
||||
# 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.
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from horizon import browsers
|
||||
|
||||
from horizon_web_ui.freezer_ui.configurations import tables
|
||||
|
||||
|
||||
class ContainerBrowser(browsers.ResourceBrowser):
|
||||
name = "backup_configuration"
|
||||
verbose_name = _("Backup Configuration")
|
||||
navigation_table_class = tables.BackupConfigsTable
|
||||
content_table_class = tables.InstancesTable
|
||||
navigable_item_name = _("Backup Configuration")
|
||||
navigation_kwarg_name = "name"
|
|
@ -1,3 +0,0 @@
|
|||
"""
|
||||
Stub file to work around django bug: https://code.djangoproject.com/ticket/7198
|
||||
"""
|
|
@ -1,24 +0,0 @@
|
|||
# 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.
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
import horizon
|
||||
from horizon_web_ui.freezer_ui import dashboard
|
||||
|
||||
|
||||
class BackupConfigsPanel(horizon.Panel):
|
||||
name = _("Configurations")
|
||||
slug = "configurations"
|
||||
|
||||
|
||||
dashboard.Freezer.register(BackupConfigsPanel)
|
|
@ -1,161 +0,0 @@
|
|||
# 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 datetime
|
||||
|
||||
from django import shortcuts
|
||||
from django.utils import safestring
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.translation import ungettext_lazy
|
||||
|
||||
from horizon import messages
|
||||
from horizon import tables
|
||||
from horizon.utils.urlresolvers import reverse
|
||||
|
||||
import horizon_web_ui.freezer_ui.api.api as freezer_api
|
||||
from horizon_web_ui.freezer_ui.django_utils import timestamp_to_string
|
||||
|
||||
|
||||
def format_last_backup(last_backup):
|
||||
last_backup_ts = datetime.datetime.fromtimestamp(last_backup)
|
||||
ten_days_later = last_backup_ts + datetime.timedelta(days=10)
|
||||
today = datetime.datetime.today()
|
||||
|
||||
if last_backup is None:
|
||||
colour = 'red'
|
||||
icon = 'fire'
|
||||
text = 'Never'
|
||||
elif ten_days_later < today:
|
||||
colour = 'orange'
|
||||
icon = 'thumbs-down'
|
||||
text = timestamp_to_string(last_backup)
|
||||
else:
|
||||
colour = 'green'
|
||||
icon = 'thumbs-up'
|
||||
text = timestamp_to_string(last_backup)
|
||||
|
||||
return safestring.mark_safe(
|
||||
'<span style="color:{}"><span class="glyphicon glyphicon-{}" aria-hidd'
|
||||
'en="true"></span> {}</span>'.format(colour, icon, text))
|
||||
|
||||
|
||||
class Restore(tables.Action):
|
||||
name = "restore"
|
||||
verbose_name = _("Restore")
|
||||
|
||||
def single(self, table, request, instance):
|
||||
messages.info(request, "Needs to be implemented")
|
||||
|
||||
def allowed(self, request, instance):
|
||||
return True
|
||||
|
||||
|
||||
class DeleteConfig(tables.DeleteAction):
|
||||
name = "delete"
|
||||
classes = ("btn-danger",)
|
||||
icon = "remove"
|
||||
help_text = _("Delete configurations are not recoverable.")
|
||||
|
||||
@staticmethod
|
||||
def action_present(count):
|
||||
return ungettext_lazy(
|
||||
u"Delete Configuration File",
|
||||
u"Delete Configuration Files",
|
||||
count
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def action_past(count):
|
||||
return ungettext_lazy(
|
||||
u"Deleted Configuration File",
|
||||
u"Deleted Configuration Files",
|
||||
count
|
||||
)
|
||||
|
||||
def delete(self, request, obj_id):
|
||||
return freezer_api.configuration_delete(request, obj_id)
|
||||
|
||||
|
||||
class CloneConfig(tables.Action):
|
||||
name = "clone"
|
||||
verbose_name = _("Clone")
|
||||
# classes = ("ajax-modal",)
|
||||
help_text = _("Clone and edit a configuration file")
|
||||
|
||||
def single(self, table, request, obj_id):
|
||||
freezer_api.configuration_clone(request, obj_id)
|
||||
return shortcuts.redirect('horizon:freezer_ui:configurations:index')
|
||||
|
||||
|
||||
class EditConfig(tables.LinkAction):
|
||||
name = "edit"
|
||||
verbose_name = _("Edit")
|
||||
classes = ("ajax-modal",)
|
||||
|
||||
def get_link_url(self, datum=None):
|
||||
return reverse("horizon:freezer_ui:configurations:configure",
|
||||
kwargs={'name': datum.config_id})
|
||||
|
||||
|
||||
def get_backup_configs_link(backup_config):
|
||||
return reverse('horizon:freezer_ui:configurations:index',
|
||||
kwargs={'config_id': backup_config.config_id})
|
||||
|
||||
|
||||
class CreateConfig(tables.LinkAction):
|
||||
name = "create"
|
||||
verbose_name = _("Create Configuration")
|
||||
url = "horizon:freezer_ui:configurations:create"
|
||||
classes = ("ajax-modal",)
|
||||
icon = "plus"
|
||||
|
||||
|
||||
class BackupConfigsTable(tables.DataTable):
|
||||
name = tables.Column("name", link=get_backup_configs_link,
|
||||
verbose_name=_("Configuration Name"))
|
||||
|
||||
def get_object_id(self, backup_config):
|
||||
return backup_config.id
|
||||
|
||||
class Meta(object):
|
||||
name = "backup_configuration"
|
||||
verbose_name = _("Backup Configurations")
|
||||
table_actions = (CreateConfig,)
|
||||
footer = False
|
||||
multi_select = False
|
||||
row_actions = (EditConfig,
|
||||
CloneConfig,
|
||||
DeleteConfig, )
|
||||
|
||||
|
||||
class ObjectFilterAction(tables.FilterAction):
|
||||
def allowed(self, request, datum):
|
||||
return bool(self.table.kwargs['config_id'])
|
||||
|
||||
|
||||
class InstancesTable(tables.DataTable):
|
||||
client = tables.Column('name', verbose_name=_("Client Name"))
|
||||
|
||||
created = tables.Column('last_backup',
|
||||
filters=(format_last_backup,),
|
||||
verbose_name=_("Last backup"))
|
||||
|
||||
def get_object_id(self, container):
|
||||
return container.name
|
||||
|
||||
class Meta(object):
|
||||
name = "clients"
|
||||
verbose_name = _("Clients")
|
||||
table_actions = (ObjectFilterAction,)
|
||||
row_actions = (Restore,)
|
||||
footer = False
|
||||
multi_select = False
|
|
@ -1,11 +0,0 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Backup Configurations" %}{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
{% include "horizon/common/_page_header.html" with title=_("Backup Configurations") %}
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block main %}
|
||||
{{ backup_configuration_browser.render }}
|
||||
{% endblock %}
|
|
@ -1,40 +0,0 @@
|
|||
# Copyright 2014 Hewlett-Packard
|
||||
#
|
||||
# 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.
|
||||
|
||||
|
||||
class Configuration(object):
|
||||
def __init__(self, data_dict):
|
||||
self.data_dict = data_dict
|
||||
|
||||
@property
|
||||
def id(self):
|
||||
return self.config_id
|
||||
|
||||
def __getattr__(self, attr):
|
||||
"""Make data_dict fields available via class interface """
|
||||
if attr in self.data_dict:
|
||||
return self.data_dict[attr]
|
||||
elif attr in self.data_dict['config_file']:
|
||||
return self.data_dict['config_file'][attr]
|
||||
else:
|
||||
return object.__getattribute__(self, attr)
|
||||
|
||||
|
||||
class Client(object):
|
||||
"""Aggregate clients and metadata """
|
||||
|
||||
def __init__(self, client):
|
||||
self.name = client
|
||||
self.clients = client
|
||||
self.client_id = client
|
|
@ -1,94 +0,0 @@
|
|||
# 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
|
||||
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from horizon import workflows
|
||||
|
||||
from horizon import browsers
|
||||
from horizon import exceptions
|
||||
import horizon_web_ui.freezer_ui.api.api as freezer_api
|
||||
import horizon_web_ui.freezer_ui.configurations.browsers as project_browsers
|
||||
import workflows.configure as configure_workflow
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ConfigureWorkflowView(workflows.WorkflowView):
|
||||
workflow_class = configure_workflow.ConfigureBackups
|
||||
|
||||
def get_object(self, *args, **kwargs):
|
||||
config_id = self.kwargs['name']
|
||||
try:
|
||||
return freezer_api.configuration_get(self.request, config_id)[0]
|
||||
except Exception:
|
||||
redirect = reverse("horizon:freezer_ui:configurations:index")
|
||||
msg = _('Unable to retrieve details.')
|
||||
exceptions.handle(self.request, msg, redirect=redirect)
|
||||
|
||||
def is_update(self):
|
||||
return 'name' in self.kwargs and bool(self.kwargs['name'])
|
||||
|
||||
def get_initial(self):
|
||||
initial = super(ConfigureWorkflowView, self).get_initial()
|
||||
if self.is_update():
|
||||
initial.update({'original_name': None})
|
||||
config = self.get_object()
|
||||
initial['name'] = config.name
|
||||
initial['container_name'] = config.container_name
|
||||
initial['config_id'] = config.config_id
|
||||
initial['src_file'] = config.src_file
|
||||
initial['levels'] = config.levels
|
||||
initial['optimize'] = config.optimize
|
||||
initial['compression'] = config.compression
|
||||
initial['encryption_password'] = config.encryption_password
|
||||
initial['start_datetime'] = config.start_datetime
|
||||
initial['interval'] = config.interval
|
||||
initial['exclude'] = config.exclude
|
||||
initial['log_file'] = config.log_file
|
||||
initial['encryption_password'] = config.encryption_password
|
||||
initial['proxy'] = config.proxy
|
||||
initial['max_priority'] = config.max_priority
|
||||
initial['clients'] = config.clients
|
||||
initial['original_name'] = config.config_id
|
||||
initial.update({'original_name': config.config_id})
|
||||
return initial
|
||||
|
||||
|
||||
class BackupConfigsView(browsers.ResourceBrowserView):
|
||||
browser_class = project_browsers.ContainerBrowser
|
||||
template_name = "freezer_ui/configurations/browser.html"
|
||||
|
||||
def get_backup_configuration_data(self):
|
||||
configurations = []
|
||||
try:
|
||||
configurations = freezer_api.configuration_list(self.request)
|
||||
except Exception:
|
||||
msg = _('Unable to retrieve configuration file list.')
|
||||
exceptions.handle(self.request, msg)
|
||||
return configurations
|
||||
|
||||
def get_clients_data(self):
|
||||
configuration = []
|
||||
try:
|
||||
if self.kwargs['config_id']:
|
||||
configuration = freezer_api.clients_in_config(
|
||||
self.request, self.kwargs['config_id'])
|
||||
|
||||
except Exception:
|
||||
msg = _('Unable to retrieve instances for this configuration.')
|
||||
exceptions.handle(self.request, msg)
|
||||
return configuration
|
|
@ -1,315 +0,0 @@
|
|||
# Copyright 2012 United States Government as represented by the
|
||||
# Administrator of the National Aeronautics and Space Administration.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Copyright 2012 Nebula, Inc.
|
||||
#
|
||||
# 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
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.views.decorators.debug import sensitive_variables # noqa
|
||||
from horizon import exceptions
|
||||
from horizon import forms
|
||||
from horizon import workflows
|
||||
|
||||
import horizon_web_ui.freezer_ui.api.api as freezer_api
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class BackupConfigurationAction(workflows.Action):
|
||||
original_name = forms.CharField(
|
||||
widget=forms.HiddenInput(),
|
||||
required=False)
|
||||
|
||||
name = forms.CharField(
|
||||
label=_("Configuration Name"),
|
||||
required=True)
|
||||
|
||||
container_name = forms.CharField(
|
||||
label=_("Swift Container Name"),
|
||||
required=True)
|
||||
|
||||
mode = forms.ChoiceField(
|
||||
help_text="Choose what you want to backup",
|
||||
required=True)
|
||||
|
||||
src_file = forms.CharField(
|
||||
label=_("Source File/Directory"),
|
||||
help_text="The file or directory you want to back up to Swift",
|
||||
required=True)
|
||||
|
||||
def populate_mode_choices(self, request, context):
|
||||
return [
|
||||
('fs', _("File system")),
|
||||
('snapshot', _("Snapshot")),
|
||||
('mongo', _("MongoDB")),
|
||||
('mysql', _("MySQL")),
|
||||
('mssql', _("Microsoft SQL Server")),
|
||||
('elastic', _("ElasticSearch")),
|
||||
('postgres', _("Postgres")),
|
||||
]
|
||||
|
||||
class Meta(object):
|
||||
name = _("Backup")
|
||||
|
||||
|
||||
class BackupConfiguration(workflows.Step):
|
||||
action_class = BackupConfigurationAction
|
||||
contributes = ('mode',
|
||||
'name',
|
||||
'container_name',
|
||||
'src_file',
|
||||
'original_name')
|
||||
|
||||
|
||||
class OptionsConfigurationAction(workflows.Action):
|
||||
levels = forms.IntegerField(
|
||||
label=_("Number of incremental backups"),
|
||||
initial=0,
|
||||
min_value=0,
|
||||
required=False,
|
||||
help_text="Set the backup level used with tar"
|
||||
" to implement incremental backup. "
|
||||
"If a level 1 is specified but no "
|
||||
"level 0 is already available, a "
|
||||
"level 0 will be done and "
|
||||
"subsequently backs to level 1. "
|
||||
"Default 0 (No Incremental)")
|
||||
|
||||
optimize = forms.ChoiceField(
|
||||
choices=[('speed', _("Speed (tar)")),
|
||||
('bandwith', "Bandwith/Space (rsync)")],
|
||||
help_text="",
|
||||
label='Optimize for...',
|
||||
required=False)
|
||||
|
||||
compression = forms.ChoiceField(
|
||||
choices=[('gzip', _("Minimum Compression (GZip/Zip)")),
|
||||
('bzip', _("Medium Compression (BZip2")),
|
||||
('xz', _("Maximum Compression (XZ)"))],
|
||||
help_text="",
|
||||
label='Compression Level',
|
||||
required=False)
|
||||
|
||||
encryption_password = forms.CharField(
|
||||
label=_("Encryption Password"), # encryption key
|
||||
widget=forms.PasswordInput(),
|
||||
help_text="",
|
||||
required=False)
|
||||
|
||||
class Meta(object):
|
||||
name = _("Options")
|
||||
|
||||
|
||||
class OptionsConfiguration(workflows.Step):
|
||||
action_class = OptionsConfigurationAction
|
||||
contributes = ('levels',
|
||||
'optimize',
|
||||
'compression',
|
||||
'encryption_password',)
|
||||
|
||||
|
||||
class ClientsConfigurationAction(workflows.MembershipAction):
|
||||
def __init__(self, request, *args, **kwargs):
|
||||
super(ClientsConfigurationAction, self).__init__(request,
|
||||
*args,
|
||||
**kwargs)
|
||||
|
||||
err_msg_configured = 'Unable to retrieve list of configured clients.'
|
||||
err_msg_all = 'Unable to retrieve list of clients.'
|
||||
|
||||
default_role_field_name = self.get_default_role_field_name()
|
||||
self.fields[default_role_field_name] = forms.CharField(required=False)
|
||||
self.fields[default_role_field_name].initial = 'member'
|
||||
|
||||
field_name = self.get_member_field_name('member')
|
||||
self.fields[field_name] = forms.MultipleChoiceField(required=False)
|
||||
|
||||
all_clients = []
|
||||
try:
|
||||
all_clients = freezer_api.client_list(request)
|
||||
except Exception:
|
||||
exceptions.handle(request, err_msg_all)
|
||||
|
||||
clients = [(c.client_id, c.name) for c in all_clients]
|
||||
|
||||
self.fields[field_name].choices = clients
|
||||
|
||||
if request.method == 'POST':
|
||||
return
|
||||
|
||||
initial_clients = []
|
||||
try:
|
||||
original_name = args[0].get('original_name', None)
|
||||
if original_name:
|
||||
configured_clients = \
|
||||
freezer_api.clients_in_config(request, original_name)
|
||||
initial_clients = [client.id for client in configured_clients]
|
||||
except Exception:
|
||||
exceptions.handle(request, err_msg_configured)
|
||||
|
||||
self.fields[field_name].initial = initial_clients
|
||||
|
||||
class Meta(object):
|
||||
name = _("Clients")
|
||||
slug = "configure_clients"
|
||||
|
||||
|
||||
class ClientsConfiguration(workflows.UpdateMembersStep):
|
||||
action_class = ClientsConfigurationAction
|
||||
help_text = _(
|
||||
"Select the clients that will be backed up using this configuration.")
|
||||
available_list_title = _("All Clients")
|
||||
members_list_title = _("Selected Clients")
|
||||
no_available_text = _("No clients found.")
|
||||
no_members_text = _("No clients selected.")
|
||||
show_roles = False
|
||||
contributes = ("clients",)
|
||||
|
||||
def contribute(self, data, context):
|
||||
if data:
|
||||
member_field_name = self.get_member_field_name('member')
|
||||
context['clients'] = data.get(member_field_name, [])
|
||||
return context
|
||||
|
||||
|
||||
class SchedulingConfigurationAction(workflows.Action):
|
||||
start_datetime = forms.CharField(
|
||||
label=_("Start Date and Time"),
|
||||
required=False,
|
||||
help_text=_("Set a start date and time for backups"))
|
||||
|
||||
interval = forms.CharField(
|
||||
label=_("Interval"),
|
||||
required=False,
|
||||
help_text=_("Repeat this configuration in an interval. e.g. 24 hours"))
|
||||
|
||||
class Meta(object):
|
||||
name = _("Scheduling")
|
||||
|
||||
|
||||
class SchedulingConfiguration(workflows.Step):
|
||||
action_class = SchedulingConfigurationAction
|
||||
contributes = ('start_datetime',
|
||||
'interval',)
|
||||
|
||||
|
||||
class AdvancedConfigurationAction(workflows.Action):
|
||||
exclude = forms.CharField(
|
||||
label=_("Exclude Files"),
|
||||
help_text="Exclude files, given as a PATTERN.Ex:"
|
||||
" --exclude '*.log' will exclude any "
|
||||
"file with name ending with .log. "
|
||||
"Default no exclude",
|
||||
required=False)
|
||||
log_file = forms.CharField(
|
||||
label=_("Log File Path"),
|
||||
help_text="Set log file. By default logs to "
|
||||
"/var/log/freezer.log If that file "
|
||||
"is not writable, freezer tries to "
|
||||
"log to ~/.freezer/freezer.log",
|
||||
required=False)
|
||||
|
||||
proxy = forms.CharField(
|
||||
label=_("Proxy URL"),
|
||||
help_text="Enforce proxy that alters system "
|
||||
"HTTP_PROXY and HTTPS_PROXY",
|
||||
widget=forms.URLInput(),
|
||||
required=False)
|
||||
|
||||
max_priority = forms.BooleanField(
|
||||
label=_("Max Priority"),
|
||||
help_text="Set the cpu process to the "
|
||||
"highest priority (i.e. -20 "
|
||||
"on Linux) and real-time for "
|
||||
"I/O. The process priority "
|
||||
"will be set only if nice and "
|
||||
"ionice are installed Default "
|
||||
"disabled. Use with caution.",
|
||||
widget=forms.CheckboxInput(),
|
||||
required=False)
|
||||
|
||||
class Meta(object):
|
||||
name = _("Advanced Configuration")
|
||||
|
||||
|
||||
class AdvancedConfiguration(workflows.Step):
|
||||
action_class = AdvancedConfigurationAction
|
||||
contributes = ('exclude',
|
||||
'log_file',
|
||||
'proxy',
|
||||
'max_priority')
|
||||
|
||||
|
||||
class ConfigureBackups(workflows.Workflow):
|
||||
slug = "configuration"
|
||||
name = _("Configuration")
|
||||
finalize_button_name = _("Save")
|
||||
success_message = _('Configuration file saved correctly.')
|
||||
failure_message = _('Unable to save configuration file.')
|
||||
success_url = "horizon:freezer_ui:configurations:index"
|
||||
|
||||
default_steps = (BackupConfiguration,
|
||||
OptionsConfiguration,
|
||||
ClientsConfiguration,
|
||||
SchedulingConfiguration,
|
||||
AdvancedConfiguration)
|
||||
|
||||
@sensitive_variables('encryption_password',
|
||||
'confirm_encryption_password')
|
||||
def handle(self, request, context):
|
||||
try:
|
||||
if context['original_name'] == '':
|
||||
freezer_api.configuration_create(
|
||||
request,
|
||||
name=context['name'],
|
||||
container_name=context['container_name'],
|
||||
src_file=context['src_file'],
|
||||
levels=context['levels'], # if empty save 0 not null
|
||||
optimize=context['optimize'],
|
||||
compression=context['compression'],
|
||||
encryption_password=context['encryption_password'],
|
||||
clients=context['clients'], # save the name of the client
|
||||
start_datetime=context['start_datetime'],
|
||||
interval=context['interval'],
|
||||
exclude=context['exclude'],
|
||||
log_file=context['log_file'],
|
||||
proxy=context['proxy'],
|
||||
max_priority=context['max_priority'],
|
||||
)
|
||||
else:
|
||||
freezer_api.configuration_update(
|
||||
request,
|
||||
config_id=context['original_name'],
|
||||
name=context['name'],
|
||||
container_name=context['container_name'],
|
||||
src_file=context['src_file'],
|
||||
levels=context['levels'], # if empty save 0 not null
|
||||
optimize=context['optimize'],
|
||||
compression=context['compression'],
|
||||
encryption_password=context['encryption_password'],
|
||||
clients=context['clients'], # save the name of the client
|
||||
start_datetime=context['start_datetime'],
|
||||
interval=context['interval'],
|
||||
exclude=context['exclude'],
|
||||
log_file=context['log_file'],
|
||||
proxy=context['proxy'],
|
||||
max_priority=context['max_priority'],
|
||||
)
|
||||
return True
|
||||
except Exception:
|
||||
exceptions.handle(request)
|
||||
return False
|
|
@ -15,17 +15,17 @@ from django.utils.translation import ugettext_lazy as _
|
|||
import horizon
|
||||
|
||||
|
||||
class Mygroup(horizon.PanelGroup):
|
||||
slug = "mygroup"
|
||||
name = _("Freezer")
|
||||
panels = ('overview', 'configurations', 'backups', 'actions')
|
||||
class FreezerDR(horizon.PanelGroup):
|
||||
slug = "freezerdr"
|
||||
name = _("Backup and Restore")
|
||||
panels = ('jobs',)
|
||||
|
||||
|
||||
class Freezer(horizon.Dashboard):
|
||||
name = _("Backup Restore DR")
|
||||
name = _("Disaster Recovery")
|
||||
slug = "freezer_ui"
|
||||
panels = (Mygroup,)
|
||||
default_panel = 'overview'
|
||||
panels = (FreezerDR,)
|
||||
default_panel = 'jobs'
|
||||
|
||||
|
||||
horizon.register(Freezer)
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
# 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
|
||||
# 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
|
||||
|
@ -11,15 +11,14 @@
|
|||
# under the License.
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
import horizon
|
||||
|
||||
import horizon_web_ui.freezer_ui.dashboard as dashboard
|
||||
from horizon import browsers
|
||||
from horizon_web_ui.freezer_ui.jobs import tables
|
||||
|
||||
|
||||
class BackupsPanel(horizon.Panel):
|
||||
name = _("Backups")
|
||||
slug = "backups"
|
||||
|
||||
|
||||
dashboard.Freezer.register(BackupsPanel)
|
||||
class ContainerBrowser(browsers.ResourceBrowser):
|
||||
name = "backup_configuration"
|
||||
verbose_name = _("Job Configuration")
|
||||
navigation_table_class = tables.JobsTable
|
||||
content_table_class = tables.ActionsTable
|
||||
navigable_item_name = _("Jobs")
|
||||
navigation_kwarg_name = "name"
|
|
@ -2,7 +2,7 @@
|
|||
# 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
|
||||
# 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
|
||||
|
@ -13,13 +13,12 @@
|
|||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
import horizon
|
||||
|
||||
from horizon_web_ui.freezer_ui import dashboard
|
||||
|
||||
|
||||
class ActionsPanel(horizon.Panel):
|
||||
class JobsPanel(horizon.Panel):
|
||||
name = _("Jobs")
|
||||
slug = "actions"
|
||||
slug = "jobs"
|
||||
|
||||
|
||||
dashboard.Freezer.register(ActionsPanel)
|
||||
dashboard.Freezer.register(JobsPanel)
|
|
@ -0,0 +1,226 @@
|
|||
# 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 datetime
|
||||
from django import shortcuts
|
||||
from django.utils import safestring
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.translation import ungettext_lazy
|
||||
|
||||
from horizon import messages
|
||||
from horizon import tables
|
||||
from horizon.utils.urlresolvers import reverse
|
||||
|
||||
import horizon_web_ui.freezer_ui.api.api as freezer_api
|
||||
from horizon_web_ui.freezer_ui.django_utils import timestamp_to_string
|
||||
|
||||
|
||||
def format_last_backup(last_backup):
|
||||
last_backup_ts = datetime.datetime.fromtimestamp(last_backup)
|
||||
ten_days_later = last_backup_ts + datetime.timedelta(days=10)
|
||||
today = datetime.datetime.today()
|
||||
|
||||
if last_backup is None:
|
||||
colour = 'red'
|
||||
icon = 'fire'
|
||||
text = 'Never'
|
||||
elif ten_days_later < today:
|
||||
colour = 'orange'
|
||||
icon = 'thumbs-down'
|
||||
text = timestamp_to_string(last_backup)
|
||||
else:
|
||||
colour = 'green'
|
||||
icon = 'thumbs-up'
|
||||
text = timestamp_to_string(last_backup)
|
||||
|
||||
return safestring.mark_safe(
|
||||
'<span style="color:{}"><span class="glyphicon glyphicon-{}" aria-hidd'
|
||||
'en="true"></span> {}</span>'.format(colour, icon, text))
|
||||
|
||||
|
||||
class Restore(tables.Action):
|
||||
name = "restore"
|
||||
verbose_name = _("Restore")
|
||||
|
||||
def single(self, table, request, instance):
|
||||
messages.info(request, "Needs to be implemented")
|
||||
|
||||
def allowed(self, request, instance):
|
||||
return True
|
||||
|
||||
|
||||
class DeleteJob(tables.DeleteAction):
|
||||
name = "delete"
|
||||
classes = ("btn-danger",)
|
||||
icon = "remove"
|
||||
help_text = _("Delete jobs is not recoverable.")
|
||||
|
||||
@staticmethod
|
||||
def action_present(count):
|
||||
return ungettext_lazy(
|
||||
u"Delete Job File",
|
||||
u"Delete Job Files",
|
||||
count
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def action_past(count):
|
||||
return ungettext_lazy(
|
||||
u"Deleted Job File",
|
||||
u"Deleted Job Files",
|
||||
count
|
||||
)
|
||||
|
||||
def delete(self, request, obj_id):
|
||||
return freezer_api.job_delete(request, obj_id)
|
||||
|
||||
|
||||
class CloneJob(tables.Action):
|
||||
name = "clone"
|
||||
verbose_name = _("Clone Job")
|
||||
help_text = _("Clone and edit a job file")
|
||||
|
||||
def single(self, table, request, obj_id):
|
||||
freezer_api.job_clone(request, obj_id)
|
||||
return shortcuts.redirect('horizon:freezer_ui:jobs:index')
|
||||
|
||||
|
||||
class EditJob(tables.LinkAction):
|
||||
name = "edit"
|
||||
verbose_name = _("Edit Job")
|
||||
classes = ("ajax-modal",)
|
||||
icon = "pencil"
|
||||
|
||||
def get_link_url(self, datum=None):
|
||||
return reverse("horizon:freezer_ui:jobs:configure",
|
||||
kwargs={'backup_name': datum.job_id})
|
||||
|
||||
|
||||
def get_backup_configs_link(backup_config):
|
||||
return reverse('horizon:freezer_ui:jobs:index',
|
||||
kwargs={'job_id': backup_config.job_id})
|
||||
|
||||
|
||||
class CreateJob(tables.LinkAction):
|
||||
name = "create"
|
||||
verbose_name = _("Create Job")
|
||||
url = "horizon:freezer_ui:jobs:create"
|
||||
classes = ("ajax-modal",)
|
||||
icon = "plus"
|
||||
|
||||
|
||||
class CreateAction(tables.LinkAction):
|
||||
name = "create_action"
|
||||
verbose_name = _("Create Action")
|
||||
url = "horizon:freezer_ui:jobs:create_action"
|
||||
classes = ("ajax-modal",)
|
||||
icon = "plus"
|
||||
|
||||
def get_link_url(self, datum=None):
|
||||
return reverse("horizon:freezer_ui:jobs:create_action",
|
||||
kwargs={'job_id': datum.job_id})
|
||||
|
||||
|
||||
class JobsTable(tables.DataTable):
|
||||
job_name = tables.Column("description",
|
||||
link=get_backup_configs_link,
|
||||
verbose_name=_("Job Name"))
|
||||
|
||||
result = tables.Column("result",
|
||||
verbose_name=_("Last Result"))
|
||||
|
||||
def get_object_id(self, backup_config):
|
||||
return backup_config.id
|
||||
|
||||
class Meta(object):
|
||||
name = "jobs"
|
||||
verbose_name = _("Jobs")
|
||||
table_actions = (CreateJob,)
|
||||
footer = False
|
||||
multi_select = False
|
||||
row_actions = (CreateAction,
|
||||
EditJob,
|
||||
CloneJob,
|
||||
DeleteJob,)
|
||||
|
||||
|
||||
class DeleteAction(tables.DeleteAction):
|
||||
name = "delete"
|
||||
classes = ("btn-danger",)
|
||||
icon = "remove"
|
||||
help_text = _("Delete actions is not recoverable.")
|
||||
|
||||
@staticmethod
|
||||
def action_present(count):
|
||||
return ungettext_lazy(
|
||||
u"Delete Action",
|
||||
u"Delete Action",
|
||||
count
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def action_past(count):
|
||||
return ungettext_lazy(
|
||||
u"Deleted action File",
|
||||
u"Deleted action Files",
|
||||
count
|
||||
)
|
||||
|
||||
def delete(self, request, obj_id):
|
||||
freezer_api.action_delete(request, obj_id)
|
||||
return reverse("horizon:freezer_ui:jobs:index")
|
||||
|
||||
|
||||
class EditAction(tables.LinkAction):
|
||||
name = "edit"
|
||||
verbose_name = _("Edit")
|
||||
classes = ("ajax-modal",)
|
||||
icon = "pencil"
|
||||
|
||||
def get_link_url(self, datum=None):
|
||||
# this is used to pass to values as an url
|
||||
# TODO: look for a way to improve this
|
||||
ids = '{0}==={1}'.format(datum.action_id, datum.job_id)
|
||||
return reverse("horizon:freezer_ui:jobs:create_action",
|
||||
kwargs={'job_id': ids})
|
||||
|
||||
|
||||
class DeleteMultipleActions(DeleteAction):
|
||||
name = "delete_multiple_actions"
|
||||
|
||||
|
||||
class ObjectFilterAction(tables.FilterAction):
|
||||
def allowed(self, request, datum):
|
||||
return bool(self.table.kwargs['job_id'])
|
||||
|
||||
|
||||
class ActionsTable(tables.DataTable):
|
||||
action_name = tables.Column('action',
|
||||
verbose_name=_("Action"))
|
||||
|
||||
backup_name = tables.Column('backup_name',
|
||||
verbose_name=_("Action Name"))
|
||||
|
||||
def get_object_id(self, container):
|
||||
# this is used to pass to values as an url
|
||||
# TODO: look for a way to improve this
|
||||
ids = '{0}==={1}'.format(container.action_id, container.job_id)
|
||||
return ids
|
||||
|
||||
class Meta(object):
|
||||
name = "status"
|
||||
verbose_name = _("Status")
|
||||
table_actions = (ObjectFilterAction,)
|
||||
row_actions = (EditAction,
|
||||
DeleteAction,)
|
||||
footer = False
|
||||
multi_select = True
|
|
@ -0,0 +1,7 @@
|
|||
{% load i18n horizon humanize %}
|
||||
|
||||
{% block help_message %}
|
||||
{% endblock %}
|
||||
|
||||
<!-- Jquery code to hide freezer inputs -->
|
||||
<script type='text/javascript' src='{{ STATIC_URL }}freezer/js/freezer.actions.action.js'></script>
|
|
@ -0,0 +1,7 @@
|
|||
{% load i18n horizon humanize %}
|
||||
|
||||
{% block help_message %}
|
||||
{% endblock %}
|
||||
|
||||
<!-- Jquery code to hide freezer inputs -->
|
||||
<script type='text/javascript' src='{{ STATIC_URL }}freezer/js/freezer.jobs.actions.action.js'></script>
|
|
@ -0,0 +1,7 @@
|
|||
{% load i18n horizon humanize %}
|
||||
|
||||
{% block help_message %}
|
||||
{% endblock %}
|
||||
|
||||
<!-- Jquery code to hide freezer inputs -->
|
||||
<script type='text/javascript' src='{{ STATIC_URL }}freezer/js/freezer.actions.advanced.js'></script>
|
|
@ -0,0 +1,21 @@
|
|||
{% load i18n horizon humanize %}
|
||||
|
||||
{% block help_message %}
|
||||
<h4>{% blocktrans %}Start and End Date Time{% endblocktrans %}</h4>
|
||||
<p>{% blocktrans %}Set a start date and time to execute jobs in ISO format:{% endblocktrans %}</p>
|
||||
<ul>
|
||||
<li>YYYY-MM-DDThh:mm:ss</li>
|
||||
</ul>
|
||||
|
||||
<h4>{% blocktrans %}Interval{% endblocktrans %}</h4>
|
||||
<p>{% blocktrans %}Set the interval in the following format:{% endblocktrans %}</p>
|
||||
<ul>
|
||||
<li>continuous</li>
|
||||
<li>N weeks</li>
|
||||
<li>N days</li>
|
||||
<li>N hours</li>
|
||||
<li>N minutes</li>
|
||||
<li>N seconds</li>
|
||||
</ul>
|
||||
|
||||
{% endblock %}
|
|
@ -0,0 +1,7 @@
|
|||
{% load i18n horizon humanize %}
|
||||
|
||||
{% block help_message %}
|
||||
{% endblock %}
|
||||
|
||||
<!-- Jquery code to hide freezer inputs -->
|
||||
<script type='text/javascript' src='{{ STATIC_URL }}freezer/js/freezer.actions.snapshot.js'></script>
|
|
@ -1,11 +1,11 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "VMs" %}{% endblock %}
|
||||
{% block title %}{% trans "Job Configurations" %}{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
{% include "horizon/common/_page_header.html" with title=_("Jobs") %}
|
||||
{% include "horizon/common/_page_header.html" with title=_("Job Configurations") %}
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block main %}
|
||||
{{ table.render }}
|
||||
{{ backup_configuration_browser.render }}
|
||||
{% endblock %}
|
|
@ -0,0 +1,21 @@
|
|||
{% load i18n horizon humanize %}
|
||||
|
||||
{% block help_message %}
|
||||
<h4>{% blocktrans %}Start and End Date Time{% endblocktrans %}</h4>
|
||||
<p>{% blocktrans %}Set a start date and time to execute jobs in ISO format:{% endblocktrans %}</p>
|
||||
<ul>
|
||||
<li>YYYY-MM-DDThh:mm:ss</li>
|
||||
</ul>
|
||||
|
||||
<h4>{% blocktrans %}Interval{% endblocktrans %}</h4>
|
||||
<p>{% blocktrans %}Set the interval in the following format:{% endblocktrans %}</p>
|
||||
<ul>
|
||||
<li>continuous</li>
|
||||
<li>N weeks</li>
|
||||
<li>N days</li>
|
||||
<li>N hours</li>
|
||||
<li>N minutes</li>
|
||||
<li>N seconds</li>
|
||||
</ul>
|
||||
|
||||
{% endblock %}
|
|
@ -0,0 +1,7 @@
|
|||
{% load i18n horizon humanize %}
|
||||
|
||||
{% block help_message %}
|
||||
{% endblock %}
|
||||
|
||||
<!-- Jquery code to hide freezer inputs -->
|
||||
<script type='text/javascript' src='{{ STATIC_URL }}freezer/js/freezer.actions.snapshot.js'></script>
|
|
@ -13,22 +13,25 @@
|
|||
from django.conf.urls import patterns
|
||||
from django.conf.urls import url
|
||||
|
||||
from horizon_web_ui.freezer_ui.configurations import views
|
||||
from horizon_web_ui.freezer_ui.jobs import views
|
||||
|
||||
|
||||
urlpatterns = patterns(
|
||||
'',
|
||||
# url(r'^$', views.BackupConfigsView.as_view(), name='test'),
|
||||
|
||||
url(r'^(?P<config_id>[^/]+)?$',
|
||||
views.BackupConfigsView.as_view(),
|
||||
url(r'^(?P<job_id>[^/]+)?$',
|
||||
views.JobsView.as_view(),
|
||||
name='index'),
|
||||
|
||||
url(r'^create/$',
|
||||
views.ConfigureWorkflowView.as_view(),
|
||||
views.JobWorkflowView.as_view(),
|
||||
name='create'),
|
||||
|
||||
url(r'^configure/(?P<name>[^/]+)?$',
|
||||
views.ConfigureWorkflowView.as_view(),
|
||||
url(r'^create_action/(?P<job_id>[^/]+)?$',
|
||||
views.ActionWorkflowView.as_view(),
|
||||
name='create_action'),
|
||||
|
||||
url(r'^configure/(?P<backup_name>[^/]+)?$',
|
||||
views.JobWorkflowView.as_view(),
|
||||
name='configure'),
|
||||
)
|
|
@ -0,0 +1,133 @@
|
|||
# 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.
|
||||
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.core.urlresolvers import reverse_lazy
|
||||
|
||||
from horizon import workflows
|
||||
|
||||
from horizon import browsers
|
||||
from horizon import exceptions
|
||||
import horizon_web_ui.freezer_ui.api.api as freezer_api
|
||||
import horizon_web_ui.freezer_ui.jobs.browsers as project_browsers
|
||||
import workflows.configure as configure_workflow
|
||||
import workflows.action as action_workflow
|
||||
from horizon_web_ui.freezer_ui.utils import create_dict_action
|
||||
|
||||
|
||||
class JobWorkflowView(workflows.WorkflowView):
|
||||
workflow_class = configure_workflow.ConfigureJob
|
||||
|
||||
def get_object(self, *args, **kwargs):
|
||||
job_id = self.kwargs['backup_name']
|
||||
try:
|
||||
return freezer_api.job_get(self.request, job_id)
|
||||
except Exception:
|
||||
redirect = reverse("horizon:freezer_ui:jobs:index")
|
||||
msg = _('Unable to retrieve details.')
|
||||
exceptions.handle(self.request, msg, redirect=redirect)
|
||||
|
||||
def is_update(self):
|
||||
return 'backup_name' in self.kwargs and \
|
||||
bool(self.kwargs['backup_name'])
|
||||
|
||||
def get_initial(self):
|
||||
initial = super(JobWorkflowView, self).get_initial()
|
||||
if self.is_update():
|
||||
initial.update({'original_name': None})
|
||||
job = self.get_object()[0]
|
||||
d = job.get_dict()
|
||||
schedule = create_dict_action(**d['job_schedule'])
|
||||
initial.update(**schedule)
|
||||
info = {k: v for k, v in d.items()
|
||||
if not k == 'job_schedule'}
|
||||
initial.update(**info)
|
||||
initial.update({'original_name': d.get('job_id', None)})
|
||||
return initial
|
||||
|
||||
|
||||
class JobsView(browsers.ResourceBrowserView):
|
||||
browser_class = project_browsers.ContainerBrowser
|
||||
template_name = "freezer_ui/jobs/browser.html"
|
||||
|
||||
def get_jobs_data(self):
|
||||
jobs = []
|
||||
try:
|
||||
jobs = freezer_api.job_list(self.request)
|
||||
except Exception:
|
||||
msg = _('Unable to retrieve job file list.')
|
||||
exceptions.handle(self.request, msg)
|
||||
return jobs
|
||||
|
||||
def get_status_data(self):
|
||||
job = []
|
||||
try:
|
||||
if self.kwargs['job_id']:
|
||||
job = freezer_api.actions_in_job(
|
||||
self.request, self.kwargs['job_id'])
|
||||
except Exception:
|
||||
msg = _('Unable to retrieve instances for this job.')
|
||||
exceptions.handle(self.request, msg)
|
||||
return job
|
||||
|
||||
|
||||
class ActionWorkflowView(workflows.WorkflowView):
|
||||
workflow_class = action_workflow.ConfigureAction
|
||||
success_url = reverse_lazy("horizon:freezer_ui:jobs:index")
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(ActionWorkflowView, self).get_context_data(**kwargs)
|
||||
job_id = self.kwargs['job_id']
|
||||
context['job_id'] = job_id
|
||||
return context
|
||||
|
||||
def get_object(self, *args, **kwargs):
|
||||
ids = self.kwargs['job_id']
|
||||
try:
|
||||
action_id, job_id = ids.split('===')
|
||||
except ValueError:
|
||||
action_id = None
|
||||
job_id = self.kwargs['job_id']
|
||||
try:
|
||||
return freezer_api.job_get(self.request, job_id)
|
||||
except Exception:
|
||||
redirect = reverse("horizon:freezer_ui:jobs:index")
|
||||
msg = _('Unable to retrieve details.')
|
||||
exceptions.handle(self.request, msg, redirect=redirect)
|
||||
|
||||
def is_update(self):
|
||||
return 'job_id' in self.kwargs and \
|
||||
bool(self.kwargs['job_id'])
|
||||
|
||||
def get_initial(self, **kwargs):
|
||||
initial = super(ActionWorkflowView, self).get_initial()
|
||||
try:
|
||||
action_id, job_id = self.kwargs['job_id'].split('===')
|
||||
except ValueError:
|
||||
job_id = self.kwargs['job_id']
|
||||
action_id = None
|
||||
|
||||
if self.is_update():
|
||||
initial.update({'original_name': None})
|
||||
job = self.get_object()[0]
|
||||
d = job.get_dict()
|
||||
for action in d['job_actions']:
|
||||
if action['action_id'] == action_id:
|
||||
actions = create_dict_action(**action)
|
||||
rules = {k: v for k, v in action.items()
|
||||
if not k == 'freezer_action'}
|
||||
initial.update(**actions['freezer_action'])
|
||||
initial.update(**rules)
|
||||
initial.update({'original_name': job.id})
|
||||
return initial
|
||||
|
|
@ -0,0 +1,580 @@
|
|||
# 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.
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from horizon import workflows
|
||||
from horizon import exceptions
|
||||
from horizon import forms
|
||||
import horizon_web_ui.freezer_ui.api.api as freezer_api
|
||||
|
||||
|
||||
class ActionConfigurationAction(workflows.Action):
|
||||
action = forms.ChoiceField(
|
||||
help_text=_("Set the action to be taken"),
|
||||
required=True)
|
||||
|
||||
mode = forms.ChoiceField(
|
||||
help_text=_("Choose what you want to backup"),
|
||||
required=False)
|
||||
|
||||
original_name = forms.CharField(
|
||||
widget=forms.HiddenInput(),
|
||||
required=False)
|
||||
|
||||
action_id = forms.CharField(
|
||||
widget=forms.HiddenInput(),
|
||||
required=False)
|
||||
|
||||
backup_name = forms.CharField(
|
||||
label=_("Backup Name"),
|
||||
required=False)
|
||||
|
||||
mysql_conf = forms.CharField(
|
||||
label=_("MySQL Configuration File"),
|
||||
help_text=_("Set the path where the MySQL configuration file "
|
||||
"is on the file system "),
|
||||
required=False)
|
||||
|
||||
sql_server_conf = forms.CharField(
|
||||
label=_("SQL Server Configuration File"),
|
||||
help_text=_("Set the path where the SQL Server configuration file"
|
||||
" is on the file system"),
|
||||
required=False)
|
||||
|
||||
path_to_backup = forms.CharField(
|
||||
label=_("Source File/Directory"),
|
||||
help_text=_("The file or directory you want to back up."),
|
||||
required=False)
|
||||
|
||||
container = forms.CharField(
|
||||
label=_("Swift Container Name"),
|
||||
required=False)
|
||||
|
||||
restore_abs_path = forms.CharField(
|
||||
label=_("Restore Absolute Path"),
|
||||
help_text=_("Set the absolute path where you"
|
||||
" want your data restored."),
|
||||
required=False)
|
||||
|
||||
restore_from_host = forms.CharField(
|
||||
label=_("Restore From Host"),
|
||||
help_text=_("Set the hostname used to identify the"
|
||||
" data you want to restore from."
|
||||
" If you want to restore data in the same"
|
||||
" host where the backup was executed just"
|
||||
" type from your shell: '$ hostname' and"
|
||||
" the output is the value that needs to"
|
||||
" be passed to this option. Mandatory"
|
||||
" with Restore"),
|
||||
required=False)
|
||||
|
||||
restore_from_date = forms.CharField(
|
||||
label=_("Restore From Date"),
|
||||
help_text=_("Set the absolute path where you want "
|
||||
"your data restored.Please provide "
|
||||
"datetime in format 'YYYY-MM-DDThh:mm:ss' "
|
||||
"i.e. '1979-10-03T23:23:23'. Make sure the "
|
||||
"'T' is between date and time"),
|
||||
required=False)
|
||||
|
||||
cinder_vol_id = forms.CharField(
|
||||
label=_("Cinder Volume ID"),
|
||||
help_text=_("Id of cinder volume for backup"),
|
||||
required=False)
|
||||
|
||||
nova_inst_id = forms.CharField(
|
||||
label=_("Nova Volume ID"),
|
||||
help_text=_("Id of nova instance for backup"),
|
||||
required=False)
|
||||
|
||||
get_object = forms.CharField(
|
||||
label=_("Get A Single Object"),
|
||||
help_text=_("The Object name you want to download on "
|
||||
"the local file system."),
|
||||
required=False)
|
||||
|
||||
dst_file = forms.CharField(
|
||||
label=_("Destination File"),
|
||||
help_text=_("The file name used to save the object "
|
||||
"on your local disk and upload file in swift."),
|
||||
required=False)
|
||||
|
||||
remove_older_than = forms.CharField(
|
||||
label=_("Remove Older Than"),
|
||||
help_text=_("Checks in the specified container for"
|
||||
" object older than the specified days."
|
||||
"If i.e. 30 is specified, it will remove"
|
||||
" the remote object older than 30 days."
|
||||
" Default False (Disabled) The option "
|
||||
"--remove-older-then is deprecated and "
|
||||
"will be removed soon"),
|
||||
required=False)
|
||||
|
||||
remove_from_date = forms.CharField(
|
||||
label=_("Remove From Date"),
|
||||
help_text=_("Checks the specified container and removes"
|
||||
" objects older than the provided datetime"
|
||||
" in the format YYYY-MM-DDThh:mm:ss "
|
||||
"i.e. 1974-03-25T23:23:23. Make sure the "
|
||||
"'T' is between date and time "),
|
||||
required=False)
|
||||
|
||||
def clean(self):
|
||||
cleaned_data = super(ActionConfigurationAction, self).clean()
|
||||
|
||||
if cleaned_data.get('action') == 'backup':
|
||||
self._check_container(cleaned_data)
|
||||
self._check_backup_name(cleaned_data)
|
||||
self._check_path_to_backup(cleaned_data)
|
||||
|
||||
elif cleaned_data.get('action') == 'restore':
|
||||
self._check_container(cleaned_data)
|
||||
self._check_backup_name(cleaned_data)
|
||||
self._check_restore_abs_path(cleaned_data)
|
||||
|
||||
return cleaned_data
|
||||
|
||||
def _check_restore_abs_path(self, cleaned_data):
|
||||
if not cleaned_data.get('restore_abs_path'):
|
||||
msg = _("You must define a path to restore.")
|
||||
self._errors['restore_abs_path'] = self.error_class([msg])
|
||||
|
||||
def _check_container(self, cleaned_data):
|
||||
if not cleaned_data.get('container'):
|
||||
msg = _("You must define a container.")
|
||||
self._errors['container'] = self.error_class([msg])
|
||||
|
||||
def _check_backup_name(self, cleaned_data):
|
||||
if not cleaned_data.get('backup_name'):
|
||||
msg = _("You must define an backup name.")
|
||||
self._errors['backup_name'] = self.error_class([msg])
|
||||
|
||||
def _check_path_to_backup(self, cleaned_data):
|
||||
if not cleaned_data.get('path_to_backup'):
|
||||
msg = _("You must define a path to backup.")
|
||||
self._errors['path_to_backup'] = self.error_class([msg])
|
||||
|
||||
def populate_mode_choices(self, request, context):
|
||||
return [
|
||||
('fs', _("File system")),
|
||||
('mongo', _("MongoDB")),
|
||||
('mysql', _("MySQL")),
|
||||
('mssql', _("Microsoft SQL Server")),
|
||||
('cinder', _("Cinder")),
|
||||
('nova', _("Nova")),
|
||||
]
|
||||
|
||||
def populate_action_choices(self, request, context):
|
||||
return [
|
||||
('', _("Select an action")),
|
||||
('backup', _("Backup")),
|
||||
('restore', _("Restore")),
|
||||
('admin', _("Admin")),
|
||||
]
|
||||
|
||||
def __init__(self, request, context, *args, **kwargs):
|
||||
self.request = request
|
||||
self.context = context
|
||||
super(ActionConfigurationAction, self).__init__(
|
||||
request, context, *args, **kwargs)
|
||||
|
||||
class Meta(object):
|
||||
name = _("Action")
|
||||
help_text_template = "freezer_ui/jobs" \
|
||||
"/_action.html"
|
||||
|
||||
|
||||
class ActionConfiguration(workflows.Step):
|
||||
action_class = ActionConfigurationAction
|
||||
contributes = ('action',
|
||||
'mode',
|
||||
'original_name',
|
||||
'backup_name',
|
||||
'mysql_conf',
|
||||
'sql_server_conf',
|
||||
'path_to_backup',
|
||||
'container',
|
||||
'restore_abs_path',
|
||||
'restore_from_host',
|
||||
'restore_from_date',
|
||||
'cinder_vol_id',
|
||||
'nova_inst_id',
|
||||
'get_object',
|
||||
'dst_file',
|
||||
'remove_older_than',
|
||||
'remove_from_date',
|
||||
'original_name',
|
||||
'action_id')
|
||||
|
||||
|
||||
class SnapshotConfigurationAction(workflows.Action):
|
||||
use_snapshot = forms.BooleanField(
|
||||
label=_("Snapshot"),
|
||||
help_text=_("Use a LVM or Shadow Copy snapshot "
|
||||
"to have point in time consistent backups"),
|
||||
widget=forms.CheckboxInput(),
|
||||
initial=False,
|
||||
required=False)
|
||||
|
||||
is_windows = forms.BooleanField(
|
||||
label=_("Job For Windows"),
|
||||
help_text=_("Is this job going to "
|
||||
"execute on windows?"),
|
||||
widget=forms.CheckboxInput(),
|
||||
required=False)
|
||||
|
||||
vssadmin = forms.BooleanField(
|
||||
label=_("VSSAdmin"),
|
||||
help_text=_("Create a backup using a snapshot on windows "
|
||||
"using vssadmin. Options are: "
|
||||
"True and False, default is True"),
|
||||
widget=forms.CheckboxInput(),
|
||||
initial=True,
|
||||
required=False)
|
||||
|
||||
lvm_auto_snap = forms.CharField(
|
||||
label=_("LVM Auto Snapshot"),
|
||||
help_text=_("Automatically guess the volume group and "
|
||||
"volume name for given PATH."),
|
||||
required=False)
|
||||
|
||||
lvm_srcvol = forms.CharField(
|
||||
label=_("Set The Volume For Snapshot"),
|
||||
help_text=_("Set the lvm volume you want to take a "
|
||||
"snapshot from. Default no volume"),
|
||||
required=False)
|
||||
|
||||
lvm_snapname = forms.CharField(
|
||||
label=_("Set A Snapshot Name"),
|
||||
help_text=_("Set the lvm snapshot name to use. "
|
||||
"If the snapshot name already exists, "
|
||||
"the old one will be used a no new one "
|
||||
"will be created. Default freezer_backup_snap."),
|
||||
required=False)
|
||||
|
||||
lvm_snapsize = forms.CharField(
|
||||
label=_("Snapshot Size"),
|
||||
help_text=_("Set the lvm snapshot size when creating "
|
||||
"a new snapshot. Please add G for Gigabytes "
|
||||
"or M for Megabytes, i.e. 500M or 8G. Default 5G."),
|
||||
required=False)
|
||||
|
||||
lvm_dirmount = forms.CharField(
|
||||
label=_("Snapshot Directory"),
|
||||
help_text=_("Set the directory you want to mount "
|
||||
"the lvm snapshot to. Default not set"),
|
||||
required=False)
|
||||
|
||||
lvm_volgroup = forms.CharField(
|
||||
label=_("Volume Group"),
|
||||
help_text=_("Specify the volume group of your logical volume."
|
||||
"This is important to mount your snapshot volume."
|
||||
"Default not set"),
|
||||
required=False)
|
||||
|
||||
class Meta(object):
|
||||
name = _("Snapshot")
|
||||
help_text_template = "freezer_ui/jobs" \
|
||||
"/_snapshot.html"
|
||||
|
||||
|
||||
class SnapshotConfiguration(workflows.Step):
|
||||
action_class = SnapshotConfigurationAction
|
||||
contributes = ('use_snapshot',
|
||||
'is_windows',
|
||||
'vssadmin',
|
||||
'lvm_auto_snap',
|
||||
'lvm_srcvol',
|
||||
'lvm_snapname',
|
||||
'lvm_snapsize',
|
||||
'lvm_dirmount',
|
||||
'lvm_volgroup',)
|
||||
|
||||
|
||||
class AdvancedConfigurationAction(workflows.Action):
|
||||
|
||||
log_file = forms.CharField(
|
||||
label=_("Log File Path"),
|
||||
help_text=_("Set log file. By default logs to "
|
||||
"/var/log/freezer.log If that file "
|
||||
"is not writable, freezer tries to "
|
||||
"log to ~/.freezer/freezer.log"),
|
||||
required=False)
|
||||
|
||||
exclude = forms.CharField(
|
||||
label=_("Exclude Files"),
|
||||
help_text=_("Exclude files, given as a PATTERN.Ex:"
|
||||
" '*.log, *.pyc' will exclude any "
|
||||
"file with name ending with .log. "
|
||||
"Default no exclude"),
|
||||
widget=forms.widgets.Textarea(),
|
||||
required=False)
|
||||
|
||||
proxy = forms.CharField(
|
||||
label=_("Proxy URL"),
|
||||
help_text=_("Enforce proxy that alters system "
|
||||
"HTTP_PROXY and HTTPS_PROXY"),
|
||||
widget=forms.URLInput(),
|
||||
required=False)
|
||||
|
||||
os_auth_ver = forms.ChoiceField(
|
||||
label=_("OpenStack Authentication Version"),
|
||||
help_text=_("Swift auth version, could be 1, 2 or 3"),
|
||||
required=False)
|
||||
|
||||
upload_limit = forms.IntegerField(
|
||||
label=_("Upload Limit"),
|
||||
help_text=_("Upload bandwidth limit in Bytes per sec."
|
||||
" Can be invoked with dimensions "
|
||||
"(10K, 120M, 10G)."),
|
||||
initial=-1,
|
||||
min_value=-1,
|
||||
required=False)
|
||||
|
||||
download_limit = forms.IntegerField(
|
||||
label=_("Download Limit"),
|
||||
help_text=_("Download bandwidth limit in Bytes per sec. "
|
||||
"Can be invoked with dimensions"
|
||||
" (10K, 120M, 10G)."),
|
||||
initial=-1,
|
||||
min_value=-1,
|
||||
required=False)
|
||||
|
||||
optimize = forms.ChoiceField(
|
||||
choices=[('speed', _("Speed (tar)")),
|
||||
('bandwidth', _("Bandwidth/Space (rsync)"))],
|
||||
help_text="",
|
||||
label=_('Optimize For...'),
|
||||
required=False)
|
||||
|
||||
compression = forms.ChoiceField(
|
||||
choices=[('gzip', _("Minimum Compression (GZip/Zip/Zlib)"))],
|
||||
help_text="",
|
||||
label=_('Compression Level'),
|
||||
required=False)
|
||||
|
||||
max_segment_size = forms.IntegerField(
|
||||
label=_("Maximum Segment Size"),
|
||||
help_text=_("Set the maximum file chunk size in bytes"
|
||||
" to upload to swift."
|
||||
" Default 67108864 bytes (64MB)"),
|
||||
initial=67108864,
|
||||
min_value=1,
|
||||
required=False)
|
||||
|
||||
hostname = forms.CharField(
|
||||
label=_("Hostname"),
|
||||
help_text=_("Set hostname to execute actions. If you are "
|
||||
"executing freezer from one host but you want"
|
||||
" to delete objects belonging to another host "
|
||||
"then you can set this option that hostname and "
|
||||
"execute appropriate actions. Default current "
|
||||
"node hostname."),
|
||||
required=False)
|
||||
|
||||
encryption_password = forms.CharField(
|
||||
label=_("Encryption Key"),
|
||||
help_text=_("Set the path where the encryption key"
|
||||
"is on the file system"),
|
||||
required=False)
|
||||
|
||||
no_incremental = forms.BooleanField(
|
||||
label=_("No Incremental"),
|
||||
help_text=_("Disable incremental feature. By default"
|
||||
" freezer build the meta data even for "
|
||||
"level 0 backup. By setting this option "
|
||||
"incremental meta data is not created at all."
|
||||
" Default disabled"),
|
||||
widget=forms.CheckboxInput(),
|
||||
initial=True,
|
||||
required=False)
|
||||
|
||||
max_level = forms.IntegerField(
|
||||
label=_("Max Level"),
|
||||
initial=0,
|
||||
min_value=0,
|
||||
help_text=_("Set the backup level used with tar to implement"
|
||||
" incremental backup. If a level 1 is specified "
|
||||
"but no level 0 is already available, a level 0"
|
||||
" will be done and subsequently backs to level 1."
|
||||
" Default 0 (No Incremental)"),
|
||||
required=False)
|
||||
|
||||
always_level = forms.IntegerField(
|
||||
label=_("Always Level"),
|
||||
initial=0,
|
||||
min_value=0,
|
||||
help_text=_("Set backup maximum level used with tar to"
|
||||
" implement incremental backup. If a level "
|
||||
"3 is specified, the backup will be executed "
|
||||
"from level 0 to level 3 and to that point "
|
||||
"always a backup level 3 will be executed. "
|
||||
"It will not restart from level 0. This option "
|
||||
"has precedence over --max-backup-level. "
|
||||
"Default False (Disabled)"),
|
||||
required=False)
|
||||
|
||||
restart_always_level = forms.IntegerField(
|
||||
label=_("Restart Always Level"),
|
||||
initial=0,
|
||||
min_value=0,
|
||||
help_text=_("Restart the backup from level 0 after n days. "
|
||||
"Valid only if --always-level option if set. "
|
||||
"If --always-level is used together with "
|
||||
"--remove-older-then, there might be the "
|
||||
"chance where the initial level 0 will be "
|
||||
"removed Default False (Disabled)"),
|
||||
required=False)
|
||||
|
||||
insecure = forms.BooleanField(
|
||||
label=_("insecure"),
|
||||
help_text=_("Allow to access swift servers without"
|
||||
" checking SSL certs."),
|
||||
widget=forms.CheckboxInput(),
|
||||
required=False)
|
||||
|
||||
dereference_symlink = forms.BooleanField(
|
||||
label=_("Follow Symlinks"),
|
||||
help_text=_("Follow hard and soft links and archive "
|
||||
"and dump the files they refer to. "
|
||||
"Default False"),
|
||||
widget=forms.CheckboxInput(),
|
||||
required=False)
|
||||
|
||||
dry_run = forms.BooleanField(
|
||||
label=_("Dry Run"),
|
||||
help_text=_("Do everything except writing or "
|
||||
"removing objects"),
|
||||
widget=forms.CheckboxInput(),
|
||||
required=False)
|
||||
|
||||
max_priority = forms.BooleanField(
|
||||
label=_("Max Priority"),
|
||||
help_text=_("Set the cpu process to the "
|
||||
"highest priority (i.e. -20 "
|
||||
"on Linux) and real-time for "
|
||||
"I/O. The process priority "
|
||||
"will be set only if nice and "
|
||||
"ionice are installed Default "
|
||||
"disabled. Use with caution."),
|
||||
widget=forms.CheckboxInput(),
|
||||
required=False)
|
||||
|
||||
quiet = forms.BooleanField(
|
||||
label=_("Quiet"),
|
||||
help_text=_("Suppress error messages"),
|
||||
widget=forms.CheckboxInput(),
|
||||
required=False)
|
||||
|
||||
def populate_os_auth_ver_choices(self, request, context):
|
||||
return [
|
||||
('2', _("v2")),
|
||||
('1', _("v1")),
|
||||
('3', _("v3")),
|
||||
]
|
||||
|
||||
class Meta(object):
|
||||
name = _("Advanced")
|
||||
help_text_template = "freezer_ui/jobs" \
|
||||
"/_advanced.html"
|
||||
|
||||
|
||||
class AdvancedConfiguration(workflows.Step):
|
||||
action_class = AdvancedConfigurationAction
|
||||
contributes = ('log_file',
|
||||
'exclude',
|
||||
'proxy',
|
||||
'os_auth_ver',
|
||||
'upload_limit',
|
||||
'download_limit',
|
||||
'optimize',
|
||||
'compression',
|
||||
'max_segment_size',
|
||||
'hostname',
|
||||
'encryption_password',
|
||||
'no_incremental',
|
||||
'max_level',
|
||||
'always_level',
|
||||
'restart_always_level',
|
||||
'insecure',
|
||||
'dereference_symlink',
|
||||
'dry_run',
|
||||
'max_priority',
|
||||
'quiet',)
|
||||
|
||||
|
||||
class RulesConfigurationAction(workflows.Action):
|
||||
max_retries = forms.IntegerField(
|
||||
label=_("Max Retries"),
|
||||
initial=0,
|
||||
min_value=0,
|
||||
help_text=_("In case of error, set the amount"
|
||||
" of retries for this job"),
|
||||
required=False)
|
||||
|
||||
max_retries_interval = forms.IntegerField(
|
||||
label=_("Max Retries Interval"),
|
||||
initial=0,
|
||||
min_value=0,
|
||||
help_text=_("Set the interval between intervals "
|
||||
"for retries in seconds"),
|
||||
required=False)
|
||||
|
||||
mandatory = forms.BooleanField(
|
||||
label=_("Mandatory"),
|
||||
help_text=_("Set this job as mandatory"),
|
||||
widget=forms.CheckboxInput(),
|
||||
required=False)
|
||||
|
||||
class Meta(object):
|
||||
name = _("Rules")
|
||||
|
||||
|
||||
class RulesConfiguration(workflows.Step):
|
||||
action_class = RulesConfigurationAction
|
||||
contributes = ('max_retries',
|
||||
'max_retries_interval',
|
||||
'mandatory')
|
||||
|
||||
|
||||
class ConfigureAction(workflows.Workflow):
|
||||
slug = "action"
|
||||
name = _("Action Configuration")
|
||||
finalize_button_name = _("Save")
|
||||
success_message = _('Action file saved correctly.')
|
||||
failure_message = _('Unable to save action file.')
|
||||
success_url = "horizon:freezer_ui:jobs:index"
|
||||
|
||||
default_steps = (ActionConfiguration,
|
||||
SnapshotConfiguration,
|
||||
RulesConfiguration,
|
||||
AdvancedConfiguration)
|
||||
|
||||
def handle(self, request, context):
|
||||
try:
|
||||
if context['is_windows']:
|
||||
client_os = 'Windows'
|
||||
else:
|
||||
client_os = 'Linux'
|
||||
|
||||
if context['use_snapshot'] and client_os == 'Windows':
|
||||
context['vssadmin'] = True
|
||||
else:
|
||||
context['vssadmin'] = False
|
||||
|
||||
if context['action_id'] == '':
|
||||
return freezer_api.action_create(request, context)
|
||||
else:
|
||||
return freezer_api.action_update(request, context)
|
||||
except Exception:
|
||||
exceptions.handle(request)
|
||||
return False
|
|
@ -0,0 +1,231 @@
|
|||
# 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.
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
import datetime
|
||||
from horizon import exceptions
|
||||
from horizon import forms
|
||||
from horizon import workflows
|
||||
|
||||
import horizon_web_ui.freezer_ui.api.api as freezer_api
|
||||
|
||||
|
||||
class ClientsConfigurationAction(workflows.Action):
|
||||
|
||||
client_id = forms.ChoiceField(
|
||||
help_text=_("Set the client for this job"),
|
||||
required=True)
|
||||
|
||||
def populate_client_id_choices(self, request, context):
|
||||
clients = []
|
||||
try:
|
||||
clients = freezer_api.client_list(request)
|
||||
except Exception:
|
||||
exceptions.handle(request, _('Error getting client list'))
|
||||
|
||||
client_id = [(c.client_id, c.hostname) for c in clients]
|
||||
client_id.insert(0, ('', _('Select A Client')))
|
||||
return client_id
|
||||
|
||||
class Meta(object):
|
||||
name = _("Clients")
|
||||
slug = "clients"
|
||||
|
||||
|
||||
class ClientsConfiguration(workflows.Step):
|
||||
action_class = ClientsConfigurationAction
|
||||
contributes = ('client_id',)
|
||||
|
||||
|
||||
class ActionsConfigurationAction(workflows.MembershipAction):
|
||||
def __init__(self, request, *args, **kwargs):
|
||||
super(ActionsConfigurationAction, self).__init__(request,
|
||||
*args,
|
||||
**kwargs)
|
||||
|
||||
original_name = args[0].get('original_name', None)
|
||||
|
||||
err_msg = _('Unable to retrieve list of actions.')
|
||||
|
||||
if original_name:
|
||||
default_role_field_name = self.get_default_role_field_name()
|
||||
self.fields[default_role_field_name] = \
|
||||
forms.CharField(required=False,
|
||||
widget=forms.HiddenInput())
|
||||
self.fields[default_role_field_name].initial = 'member'
|
||||
|
||||
actions = self.get_member_field_name('member')
|
||||
self.fields[actions] = \
|
||||
forms.MultipleChoiceField(required=False,
|
||||
widget=forms.HiddenInput())
|
||||
else:
|
||||
default_role_field_name = self.get_default_role_field_name()
|
||||
self.fields[default_role_field_name] = \
|
||||
forms.CharField(required=False)
|
||||
self.fields[default_role_field_name].initial = 'member'
|
||||
|
||||
actions = self.get_member_field_name('member')
|
||||
self.fields[actions] = forms.MultipleChoiceField(required=False)
|
||||
|
||||
all_actions = []
|
||||
try:
|
||||
all_actions = freezer_api.action_list(request)
|
||||
except Exception:
|
||||
exceptions.handle(request, err_msg)
|
||||
|
||||
all_actions = [(a.action_id, a.freezer_action['backup_name'])
|
||||
for a in all_actions]
|
||||
|
||||
self.fields[actions].choices = all_actions
|
||||
|
||||
initial_actions = []
|
||||
|
||||
if request.method == 'POST':
|
||||
return
|
||||
|
||||
try:
|
||||
if original_name:
|
||||
configured_actions = \
|
||||
freezer_api.actions_in_job(request, original_name)
|
||||
initial_actions = [a.action_id for a in configured_actions]
|
||||
except Exception:
|
||||
exceptions.handle(request, err_msg)
|
||||
|
||||
self.fields[actions].initial = initial_actions
|
||||
|
||||
class Meta(object):
|
||||
name = _("Actions")
|
||||
slug = "selected_actions"
|
||||
help_text_template = "freezer_ui/jobs" \
|
||||
"/_actions.html"
|
||||
|
||||
|
||||
class ActionsConfiguration(workflows.UpdateMembersStep):
|
||||
action_class = ActionsConfigurationAction
|
||||
help_text = _(
|
||||
"Select the clients that will be backed up using this configuration.")
|
||||
available_list_title = _("All Actions")
|
||||
members_list_title = _("Selected Actions")
|
||||
no_available_text = _("No actions found.")
|
||||
no_members_text = _("No actions selected.")
|
||||
show_roles = False
|
||||
contributes = ("actions",)
|
||||
|
||||
def contribute(self, data, context):
|
||||
if data:
|
||||
member_field_name = self.get_member_field_name('member')
|
||||
context['actions'] = data.get(member_field_name, [])
|
||||
return context
|
||||
|
||||
|
||||
class SchedulingConfigurationAction(workflows.Action):
|
||||
start_datetime = forms.CharField(
|
||||
label=_("Start Date and Time"),
|
||||
required=False,
|
||||
help_text=_(""))
|
||||
|
||||
interval = forms.CharField(
|
||||
label=_("Interval"),
|
||||
required=False,
|
||||
help_text=_("Repeat this configuration in a minutes interval."))
|
||||
|
||||
end_datetime = forms.CharField(
|
||||
label=_("End Date and Time"),
|
||||
required=False,
|
||||
help_text=_(""))
|
||||
|
||||
def __init__(self, request, context, *args, **kwargs):
|
||||
self.request = request
|
||||
self.context = context
|
||||
super(SchedulingConfigurationAction, self).__init__(
|
||||
request, context, *args, **kwargs)
|
||||
|
||||
def clean(self):
|
||||
cleaned_data = super(SchedulingConfigurationAction, self).clean()
|
||||
self._check_start_datetime(cleaned_data)
|
||||
self._check_end_datetime(cleaned_data)
|
||||
return cleaned_data
|
||||
|
||||
def _validate_iso_format(self, start_date):
|
||||
try:
|
||||
return datetime.datetime.strptime(
|
||||
start_date, "%Y-%m-%dT%H:%M:%S")
|
||||
except ValueError:
|
||||
return False
|
||||
|
||||
def _check_start_datetime(self, cleaned_data):
|
||||
if cleaned_data.get('start_datetime') and not \
|
||||
self._validate_iso_format(cleaned_data.get('start_datetime')):
|
||||
msg = _("Start date time is not in ISO format.")
|
||||
self._errors['start_datetime'] = self.error_class([msg])
|
||||
|
||||
def _check_end_datetime(self, cleaned_data):
|
||||
if cleaned_data.get('end_datetime') and not \
|
||||
self._validate_iso_format(cleaned_data.get('end_datetime')):
|
||||
msg = _("End date time is not in ISO format.")
|
||||
self._errors['end_datetime'] = self.error_class([msg])
|
||||
|
||||
class Meta(object):
|
||||
name = _("Scheduling")
|
||||
slug = "scheduling"
|
||||
help_text_template = "freezer_ui/jobs" \
|
||||
"/_scheduling.html"
|
||||
|
||||
|
||||
class SchedulingConfiguration(workflows.Step):
|
||||
action_class = SchedulingConfigurationAction
|
||||
contributes = ('start_datetime',
|
||||
'interval',
|
||||
'end_datetime')
|
||||
|
||||
|
||||
class InfoConfigurationAction(workflows.Action):
|
||||
description = forms.CharField(
|
||||
label=_("Job Name"),
|
||||
help_text=_("Set a short description for this job"),
|
||||
required=True)
|
||||
|
||||
original_name = forms.CharField(
|
||||
widget=forms.HiddenInput(),
|
||||
required=False)
|
||||
|
||||
class Meta(object):
|
||||
name = _("Job Info")
|
||||
slug = "info"
|
||||
|
||||
|
||||
class InfoConfiguration(workflows.Step):
|
||||
action_class = InfoConfigurationAction
|
||||
contributes = ('description',
|
||||
'original_name')
|
||||
|
||||
|
||||
class ConfigureJob(workflows.Workflow):
|
||||
slug = "job"
|
||||
name = _("Job Configuration")
|
||||
finalize_button_name = _("Save")
|
||||
success_message = _('Job file saved correctly.')
|
||||
failure_message = _('Unable to save job file.')
|
||||
success_url = "horizon:freezer_ui:jobs:index"
|
||||
default_steps = (InfoConfiguration,
|
||||
ClientsConfiguration,
|
||||
SchedulingConfiguration)
|
||||
|
||||
def handle(self, request, context):
|
||||
try:
|
||||
if context['original_name'] == '':
|
||||
return freezer_api.job_create(request, context)
|
||||
else:
|
||||
return freezer_api.job_edit(request, context)
|
||||
except Exception:
|
||||
exceptions.handle(request)
|
||||
return False
|
|
@ -1 +0,0 @@
|
|||
__author__ = 'jonas'
|
|
@ -1,3 +0,0 @@
|
|||
"""
|
||||
Stub file to work around django bug: https://code.djangoproject.com/ticket/7198
|
||||
"""
|
|
@ -1,24 +0,0 @@
|
|||
# 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.
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
import horizon
|
||||
from horizon_web_ui.freezer_ui import dashboard
|
||||
|
||||
|
||||
class OverviewPanel(horizon.Panel):
|
||||
name = _("Overview")
|
||||
slug = "overview"
|
||||
|
||||
|
||||
dashboard.Freezer.register(OverviewPanel)
|
|
@ -1,112 +0,0 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Backup Restore DR Overview" %}{% endblock %}
|
||||
|
||||
{% block css %}
|
||||
{% include "_stylesheets.html" %}
|
||||
<link href='{{ STATIC_URL }}freezer/css/freezer.css' type='text/css' media='screen' rel='stylesheet' />
|
||||
{% endblock %}
|
||||
|
||||
<noscript>
|
||||
{% trans "This pane needs javascript support." %}
|
||||
</noscript>
|
||||
|
||||
{% block main %}
|
||||
|
||||
<div class="quota-dynamic">
|
||||
<h3 class="quota-heading">{% trans "Overview" %}</h3>
|
||||
|
||||
<div class="d3_quota_bar">
|
||||
<div class="d3_pie_chart_usage" data-used="30"></div>
|
||||
<strong>{% trans "Nodes without backup in the last week" %}<br />
|
||||
18 of 50
|
||||
</strong>
|
||||
</div>
|
||||
|
||||
<div class="d3_quota_bar">
|
||||
<div class="d3_pie_chart_usage" data-used="70"></div>
|
||||
<strong>{% trans "Total space used for backups" %}<br />
|
||||
150gb of 200gb
|
||||
</strong>
|
||||
</div>
|
||||
|
||||
<div class="d3_quota_bar">
|
||||
<div class="d3_pie_chart_usage" data-used="0"></div>
|
||||
<strong>{% trans "Storage price per month" %}<br />
|
||||
60 dls
|
||||
</strong>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="quota-dynamic">
|
||||
<h3 class="quota-heading">{% trans "Backup Summary" %}</h3>
|
||||
|
||||
<div class="d3_quota_bar">
|
||||
<div class="d3_pie_chart_usage" data-used="50"></div>
|
||||
<strong>{% trans "Backups older than 1 week" %}<br />
|
||||
60 of 120
|
||||
</strong>
|
||||
</div>
|
||||
|
||||
<div class="d3_quota_bar">
|
||||
<div class="d3_pie_chart_usage" data-used="7"></div>
|
||||
<strong>{% trans "Average backup size" %}<br />
|
||||
7gb for 18 backups
|
||||
</strong>
|
||||
</div>
|
||||
|
||||
<div class="d3_quota_bar">
|
||||
<div class="d3_pie_chart_usage" data-used="18"></div>
|
||||
<strong>{% trans "Average backup time" %}<br />
|
||||
18 min
|
||||
</strong>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="quota-dynamic">
|
||||
<h3 class="quota-heading">{% trans "Restore Summary" %}</h3>
|
||||
|
||||
<div class="d3_quota_bar">
|
||||
<div class="d3_pie_chart_usage" data-used="7"></div>
|
||||
<strong>{% trans "Average restore time" %}<br />
|
||||
15 min
|
||||
</strong>
|
||||
</div>
|
||||
|
||||
<div class="d3_quota_bar">
|
||||
<div class="d3_pie_chart_usage" data-used="0"></div>
|
||||
<strong>{% trans "Restore ratio success to failure" %}<br />
|
||||
100% success
|
||||
</strong>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="quota-dynamic">
|
||||
<h3 class="quota-heading">{% trans "OS Summary" %}</h3>
|
||||
|
||||
</div>
|
||||
|
||||
<div id='dashboard'>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<script src="http://d3js.org/d3.v3.min.js"></script>
|
||||
|
||||
<script type='text/javascript' src='{{ STATIC_URL }}freezer/js/dashboard.js'></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
var freqData=[
|
||||
{State:'Linux',freq:{binary:6902, text:12085, media:500}}
|
||||
,{State:'Windows',freq:{binary:6500, text:5200, media:100}}
|
||||
,{State:'Mac OS',freq:{binary:200, text:1000, media:1500}}
|
||||
];
|
||||
|
||||
dashboard('#dashboard',freqData);
|
||||
|
||||
</script>
|
||||
{% endblock %}
|
|
@ -1,21 +0,0 @@
|
|||
# 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.
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from django.views import generic
|
||||
|
||||
|
||||
class IndexView(generic.TemplateView):
|
||||
name = _("Overview")
|
||||
slug = "overview"
|
||||
template_name = ("freezer_ui/overview/overview.html")
|
|
@ -0,0 +1,131 @@
|
|||
function hideEverything() {
|
||||
// Common controls
|
||||
$("#id_backup_name").closest(".form-group").hide();
|
||||
$("#id_container").closest(".form-group").hide();
|
||||
$("#id_path_to_backup").closest(".form-group").hide();
|
||||
|
||||
// Backup specific controls
|
||||
$("#id_mysql_conf").closest(".form-group").hide();
|
||||
$("#id_mode").closest(".form-group").hide();
|
||||
$("#id_sql_server_conf").closest(".form-group").hide();
|
||||
$("#id_cinder_vol_id").closest(".form-group").hide();
|
||||
$("#id_nova_inst_id").closest(".form-group").hide();
|
||||
|
||||
// Restore specific controls
|
||||
$("#id_restore_abs_path").closest(".form-group").hide();
|
||||
$("#id_restore_from_host").closest(".form-group").hide();
|
||||
$("#id_restore_from_date").closest(".form-group").hide();
|
||||
|
||||
// Admin specific controls
|
||||
$("#id_remove_older_than").closest(".form-group").hide();
|
||||
$("#id_remove_from_date").closest(".form-group").hide();
|
||||
$("#id_get_object").closest(".form-group").hide();
|
||||
$("#id_dst_file").closest(".form-group").hide();
|
||||
}
|
||||
|
||||
function showAdminOptions() {
|
||||
$("#id_remove_older_than").closest(".form-group").show();
|
||||
$("#id_remove_from_date").closest(".form-group").show();
|
||||
$("#id_get_object").closest(".form-group").show();
|
||||
$("#id_dst_file").closest(".form-group").show();
|
||||
}
|
||||
|
||||
function showBackupOptions() {
|
||||
$("#id_is_windows").closest(".form-group").show();
|
||||
$("#id_mode").closest(".form-group").show();
|
||||
$("#id_container").closest(".form-group").show();
|
||||
$("#id_path_to_backup").closest(".form-group").show();
|
||||
$("#id_backup_name").closest(".form-group").show();
|
||||
}
|
||||
|
||||
function showRestoreOptions() {
|
||||
$("#id_container").closest(".form-group").show();
|
||||
$("#id_backup_name").closest(".form-group").show();
|
||||
$("#id_restore_abs_path").closest(".form-group").show();
|
||||
$("#id_restore_from_host").closest(".form-group").show();
|
||||
$("#id_restore_from_date").closest(".form-group").show();
|
||||
}
|
||||
|
||||
function showNovaOptions() {
|
||||
$("#id_mode").closest(".form-group").show();
|
||||
$("#id_nova_inst_id").closest(".form-group").show();
|
||||
$("#id_backup_name").closest(".form-group").show();
|
||||
$("#id_container").closest(".form-group").show();
|
||||
}
|
||||
|
||||
function showCinderOptions() {
|
||||
$("#id_mode").closest(".form-group").show();
|
||||
$("#id_cinder_vol_id").closest(".form-group").show();
|
||||
$("#id_backup_name").closest(".form-group").show();
|
||||
$("#id_container").closest(".form-group").show();
|
||||
}
|
||||
|
||||
hideEverything();
|
||||
|
||||
$("#id_action").change(function() {
|
||||
// Update the inputs according freezer action
|
||||
|
||||
if ($("#id_action").val() == 'backup') {
|
||||
hideEverything();
|
||||
showBackupOptions();
|
||||
}
|
||||
else if ($("#id_action").val() == 'restore') {
|
||||
hideEverything();
|
||||
showRestoreOptions();
|
||||
}
|
||||
else if ($("#id_action").val() == 'admin') {
|
||||
hideEverything();
|
||||
showAdminOptions();
|
||||
}
|
||||
else {
|
||||
hideEverything();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
$("#id_mode").change(function() {
|
||||
if ($("#id_action").val() == 'backup') {
|
||||
if ($("#id_mode").val() == 'fs') {
|
||||
hideEverything();
|
||||
showBackupOptions();
|
||||
$("#id_advanced_configuration").closest(".form-group").show();
|
||||
}
|
||||
else if ($("#id_mode").val() == 'mysql') {
|
||||
hideEverything();
|
||||
showBackupOptions();
|
||||
$("#id_mysql_conf").closest(".form-group").show();
|
||||
$("#id_sql_server_conf").closest(".form-group").hide();
|
||||
$("#id_advanced_configuration").closest(".form-group").show();
|
||||
}
|
||||
else if ($("#id_mode").val() == 'mssql') {
|
||||
hideEverything();
|
||||
showBackupOptions();
|
||||
$("#id_sql_server_conf").closest(".form-group").show();
|
||||
$("#id_mysql_conf").closest(".form-group").hide();
|
||||
$("#id_advanced_configuration").closest(".form-group").show();
|
||||
}
|
||||
else if ($("#id_mode").val() == 'mongo') {
|
||||
hideEverything();
|
||||
showBackupOptions();
|
||||
$("#id_sql_server_conf").closest(".form-group").hide();
|
||||
$("#id_mysql_conf").closest(".form-group").hide();
|
||||
$("#id_advanced_configuration").closest(".form-group").show();
|
||||
}
|
||||
else if ($("#id_mode").val() == 'cinder') {
|
||||
hideEverything();
|
||||
showCinderOptions();
|
||||
$("#id_cinder_vol_id").closest(".form-group").show().addClass("required");
|
||||
$("#id_advanced_configuration").closest(".form-group").show();
|
||||
}
|
||||
else if ($("#id_mode").val() == 'nova') {
|
||||
hideEverything();
|
||||
showNovaOptions();
|
||||
$("#id_nova_inst_id").closest(".form-group").show().addClass("required");
|
||||
$("#id_advanced_configuration").closest(".form-group").show();
|
||||
}
|
||||
else {
|
||||
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
function hideIncrementalOptions() {
|
||||
$("#id_max_level").closest(".form-group").hide();
|
||||
$("#id_always_level").closest(".form-group").hide();
|
||||
$("#id_restart_always_level").closest(".form-group").hide();
|
||||
}
|
||||
|
||||
$("#id_no_incremental").click(function() {
|
||||
if ($("#id_no_incremental").is(":checked")) {
|
||||
$("#id_max_level").closest(".form-group").hide();
|
||||
$("#id_always_level").closest(".form-group").hide();
|
||||
$("#id_restart_always_level").closest(".form-group").hide();
|
||||
}
|
||||
else {
|
||||
$("#id_max_level").closest(".form-group").show();
|
||||
$("#id_always_level").closest(".form-group").show();
|
||||
$("#id_restart_always_level").closest(".form-group").show();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
hideIncrementalOptions();
|
|
@ -0,0 +1,81 @@
|
|||
function hideOptions() {
|
||||
// Snapshot specific controls
|
||||
$("#id_is_windows").closest(".form-group").hide();
|
||||
$("#id_lvm_auto_snap").closest(".form-group").hide();
|
||||
$("#id_lvm_srcvol").closest(".form-group").hide();
|
||||
$("#id_lvm_snapname").closest(".form-group").hide();
|
||||
$("#id_lvm_snapsize").closest(".form-group").hide();
|
||||
$("#id_lvm_dirmount").closest(".form-group").hide();
|
||||
$("#id_lvm_volgroup").closest(".form-group").hide();
|
||||
$("#id_vssadmin").closest(".form-group").hide();
|
||||
}
|
||||
|
||||
function is_windows() {
|
||||
if ($("#id_is_windows").is(":checked")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
function showWindowsSnapshotOptions() {
|
||||
$("#id_vssadmin").closest(".form-group").show();
|
||||
}
|
||||
|
||||
function hideWindowsSnapshotOptions() {
|
||||
$("#id_vssadmin").closest(".form-group").hide();
|
||||
}
|
||||
|
||||
function showLinuxSnapshotOptions() {
|
||||
$("#id_lvm_auto_snap").closest(".form-group").show();
|
||||
$("#id_lvm_srcvol").closest(".form-group").show();
|
||||
$("#id_lvm_snapname").closest(".form-group").show();
|
||||
$("#id_lvm_snapsize").closest(".form-group").show();
|
||||
$("#id_lvm_dirmount").closest(".form-group").show();
|
||||
$("#id_lvm_volgroup").closest(".form-group").show();
|
||||
}
|
||||
|
||||
function hideLinuxSnapshotOptions() {
|
||||
$("#id_lvm_srcvol").closest(".form-group").hide();
|
||||
$("#id_lvm_snapname").closest(".form-group").hide();
|
||||
$("#id_lvm_snapsize").closest(".form-group").hide();
|
||||
$("#id_lvm_dirmount").closest(".form-group").hide();
|
||||
$("#id_lvm_volgroup").closest(".form-group").hide();
|
||||
$("#id_lvm_auto_snap").closest(".form-group").hide();
|
||||
}
|
||||
|
||||
function hideSnapshotOptions() {
|
||||
hideWindowsSnapshotOptions();
|
||||
hideLinuxSnapshotOptions();
|
||||
$("#id_is_windows").closest(".form-group").hide();
|
||||
}
|
||||
|
||||
function showSnapshotOptions() {
|
||||
$("#id_is_windows").closest(".form-group").show();
|
||||
if (is_windows()) {
|
||||
hideLinuxSnapshotOptions();
|
||||
showWindowsSnapshotOptions();
|
||||
}
|
||||
else {
|
||||
hideWindowsSnapshotOptions();
|
||||
showLinuxSnapshotOptions();
|
||||
}
|
||||
}
|
||||
|
||||
hideOptions();
|
||||
|
||||
$("#id_use_snapshot").click(function() {
|
||||
if ($("#id_use_snapshot").is(":checked")) {
|
||||
showSnapshotOptions();
|
||||
}
|
||||
else {
|
||||
hideSnapshotOptions();
|
||||
}
|
||||
});
|
||||
|
||||
$("#id_is_windows").click(function() {
|
||||
if ($("#id_use_snapshot").is(":checked")) {
|
||||
showSnapshotOptions();
|
||||
}
|
||||
else {
|
||||
hideSnapshotOptions();
|
||||
}
|
||||
});
|
|
@ -0,0 +1,361 @@
|
|||
/* Launch Jobs workflow */
|
||||
|
||||
function hideEverything() {
|
||||
// Common controls
|
||||
$("#id_is_windows").closest(".form-group").hide();
|
||||
$("#id_backup_name").closest(".form-group").hide();
|
||||
$("#id_container").closest(".form-group").hide();
|
||||
$("#id_path_to_backup").closest(".form-group").hide();
|
||||
$("#id_insecure").closest(".form-group").hide();
|
||||
$("#id_os_auth_ver").closest(".form-group").hide();
|
||||
$("#id_dry_run").closest(".form-group").hide();
|
||||
$("#id_encryption_password").closest(".form-group").hide();
|
||||
$("#id_exclude").closest(".form-group").hide();
|
||||
$("#id_log_file").closest(".form-group").hide();
|
||||
$("#id_proxy").closest(".form-group").hide();
|
||||
$("#id_max_priority").closest(".form-group").hide();
|
||||
$("#id_quiet").closest(".form-group").hide();
|
||||
$("#id_advanced_configuration").closest(".form-group").hide();
|
||||
$("#id_description").closest(".form-group").hide();
|
||||
$("#id_client_id").closest(".form-group").hide();
|
||||
$("#id_tag").closest(".form-group").hide();
|
||||
|
||||
|
||||
// Backup specific controls
|
||||
$("#id_mysql_conf").closest(".form-group").hide();
|
||||
$("#id_compression").closest(".form-group").hide();
|
||||
$("#id_optimize").closest(".form-group").hide();
|
||||
$("#id_upload_limit").closest(".form-group").hide();
|
||||
$("#id_dereference_symlink").closest(".form-group").hide();
|
||||
$("#id_max_segment_size").closest(".form-group").hide();
|
||||
$("#id_dst_file").closest(".form-group").hide();
|
||||
$("#id_mode").closest(".form-group").hide();
|
||||
$("#id_sql_server_conf").closest(".form-group").hide();
|
||||
$("#id_cinder_vol_id").closest(".form-group").hide();
|
||||
$("#id_nova_inst_id").closest(".form-group").hide();
|
||||
$("#id_max_level").closest(".form-group").hide();
|
||||
$("#id_always_level").closest(".form-group").hide();
|
||||
$("#id_restart_always_level").closest(".form-group").hide();
|
||||
$("#id_no_incremental").closest(".form-group").hide();
|
||||
$("#id_hostname").closest(".form-group").hide();
|
||||
$("#id_upload").closest(".form-group").hide();
|
||||
|
||||
// Snapshot specific controls
|
||||
$("#id_use_snapshot").closest(".form-group").hide();
|
||||
$("#id_lvm_auto_snap").closest(".form-group").hide();
|
||||
$("#id_lvm_srcvol").closest(".form-group").hide();
|
||||
$("#id_lvm_snapname").closest(".form-group").hide();
|
||||
$("#id_lvm_snapsize").closest(".form-group").hide();
|
||||
$("#id_lvm_dirmount").closest(".form-group").hide();
|
||||
$("#id_lvm_volgroup").closest(".form-group").hide();
|
||||
$("#id_vssadmin").closest(".form-group").hide();
|
||||
|
||||
// Restore specific controls
|
||||
$("#id_download_limit").closest(".form-group").hide();
|
||||
$("#id_restore_abs_path").closest(".form-group").hide();
|
||||
$("#id_restore_from_host").closest(".form-group").hide();
|
||||
$("#id_restore_from_date").closest(".form-group").hide();
|
||||
|
||||
// Admin specific controls
|
||||
$("#id_remove_older_than").closest(".form-group").hide();
|
||||
$("#id_remove_from_date").closest(".form-group").hide();
|
||||
$("#id_get_object").closest(".form-group").hide();
|
||||
|
||||
}
|
||||
|
||||
function showAdminOptions() {
|
||||
$("#id_remove_older_than").closest(".form-group").show();
|
||||
$("#id_remove_from_date").closest(".form-group").show();
|
||||
$("#id_get_object").closest(".form-group").show();
|
||||
}
|
||||
|
||||
function showBackupOptions() {
|
||||
$("#id_is_windows").closest(".form-group").show();
|
||||
$("#id_mode").closest(".form-group").show();
|
||||
$("#id_upload").closest(".form-group").show();
|
||||
$("#id_container").closest(".form-group").show();
|
||||
$("#id_use_snapshot").closest(".form-group").show();
|
||||
$("#id_path_to_backup").closest(".form-group").show();
|
||||
$("#id_backup_name").closest(".form-group").show();
|
||||
$("#id_dereference_symlink").closest(".form-group").show();
|
||||
$("#id_os_auth_ver").closest(".form-group").show();
|
||||
$("#id_upload_limit").closest(".form-group").show();
|
||||
$("#id_max_priority").closest(".form-group").show();
|
||||
$("#id_quiet").closest(".form-group").show();
|
||||
$("#id_proxy").closest(".form-group").show();
|
||||
$("#id_log_file").closest(".form-group").show();
|
||||
$("#id_exclude").closest(".form-group").show();
|
||||
$("#id_optimize").closest(".form-group").show();
|
||||
$("#id_compression").closest(".form-group").show();
|
||||
$("#id_encryption_password").closest(".form-group").show();
|
||||
$("#id_max_segment_size").closest(".form-group").show();
|
||||
$("#id_no_incremental").closest(".form-group").show();
|
||||
$("#id_hostname").closest(".form-group").show();
|
||||
|
||||
$("#id_container").closest(".form-group").addClass("required");
|
||||
|
||||
}
|
||||
|
||||
function showRestoreOptions() {
|
||||
$("#id_container").closest(".form-group").show();
|
||||
$("#id_backup_name").closest(".form-group").show();
|
||||
$("#id_restore_abs_path").closest(".form-group").show();
|
||||
$("#id_restore_from_host").closest(".form-group").show();
|
||||
$("#id_restore_from_date").closest(".form-group").show();
|
||||
$("#id_os_auth_ver").closest(".form-group").show();
|
||||
$("#id_download_limit").closest(".form-group").show();
|
||||
$("#id_max_priority").closest(".form-group").show();
|
||||
$("#id_quiet").closest(".form-group").show();
|
||||
$("#id_proxy").closest(".form-group").show();
|
||||
$("#id_log_file").closest(".form-group").show();
|
||||
}
|
||||
|
||||
function showLinuxSnapshotOptions() {
|
||||
$("#id_lvm_auto_snap").closest(".form-group").show();
|
||||
$("#id_lvm_srcvol").closest(".form-group").show();
|
||||
$("#id_lvm_snapname").closest(".form-group").show();
|
||||
$("#id_lvm_snapsize").closest(".form-group").show();
|
||||
$("#id_lvm_dirmount").closest(".form-group").show();
|
||||
$("#id_lvm_volgroup").closest(".form-group").show();
|
||||
}
|
||||
|
||||
function hideLinuxSnapshotOptions() {
|
||||
$("#id_lvm_srcvol").closest(".form-group").hide();
|
||||
$("#id_lvm_snapname").closest(".form-group").hide();
|
||||
$("#id_lvm_snapsize").closest(".form-group").hide();
|
||||
$("#id_lvm_dirmount").closest(".form-group").hide();
|
||||
$("#id_lvm_volgroup").closest(".form-group").hide();
|
||||
$("#id_lvm_auto_snap").closest(".form-group").hide();
|
||||
}
|
||||
|
||||
function showWindowsSnapshotOptions() {
|
||||
// TODO: windows doesn't need to display this option
|
||||
// because is redundant
|
||||
$("#id_vssadmin").closest(".form-group").show();
|
||||
}
|
||||
|
||||
function hideWindowsSnapshotOptions() {
|
||||
$("#id_vssadmin").closest(".form-group").hide();
|
||||
}
|
||||
|
||||
function is_windows() {
|
||||
if ($("#id_is_windows").is(":checked")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
function showSnapshotOptions() {
|
||||
if (is_windows()) {
|
||||
hideLinuxSnapshotOptions();
|
||||
showWindowsSnapshotOptions();
|
||||
}
|
||||
else {
|
||||
hideWindowsSnapshotOptions();
|
||||
showLinuxSnapshotOptions();
|
||||
}
|
||||
}
|
||||
|
||||
function hideSnapshotOptions() {
|
||||
hideWindowsSnapshotOptions();
|
||||
hideLinuxSnapshotOptions();
|
||||
}
|
||||
|
||||
function showNovaOptions() {
|
||||
$("#id_client_id").closest(".form-group").show();
|
||||
$("#id_mode").closest(".form-group").show();
|
||||
$("#id_proxy").closest(".form-group").show();
|
||||
$("#id_nova_inst_id").closest(".form-group").show();
|
||||
$("#id_backup_name").closest(".form-group").show();
|
||||
$("#id_container").closest(".form-group").show();
|
||||
$("#id_nova_inst_id").closest(".form-group").show().addClass("required");
|
||||
}
|
||||
|
||||
function showCinderOptions() {
|
||||
$("#id_client_id").closest(".form-group").show();
|
||||
$("#id_mode").closest(".form-group").show();
|
||||
$("#id_proxy").closest(".form-group").show();
|
||||
$("#id_cinder_vol_id").closest(".form-group").show();
|
||||
$("#id_backup_name").closest(".form-group").show();
|
||||
$("#id_container").closest(".form-group").show();
|
||||
$("#id_path_to_backup").closest(".form-group").show();
|
||||
$("#id_cinder_vol_id").closest(".form-group").show().addClass("required");
|
||||
}
|
||||
|
||||
function showIncrementalOptions() {
|
||||
$("#id_max_level").closest(".form-group").show();
|
||||
$("#id_always_level").closest(".form-group").show();
|
||||
$("#id_restart_always_level").closest(".form-group").show();
|
||||
}
|
||||
|
||||
function hideIncrementalOptions() {
|
||||
$("#id_max_level").closest(".form-group").hide();
|
||||
$("#id_always_level").closest(".form-group").hide();
|
||||
$("#id_restart_always_level").closest(".form-group").hide();
|
||||
$("#id_advanced_configuration").closest(".form-group").show();
|
||||
}
|
||||
|
||||
function showAdvancedConfigurationOptions() {
|
||||
hideEverything();
|
||||
showBackupOptions();
|
||||
$("#id_tag").closest(".form-group").show();
|
||||
}
|
||||
|
||||
function hideAdvancedConfigurationOptions() {
|
||||
hideEverything();
|
||||
hideIncrementalOptions();
|
||||
$("#id_backup_name").closest(".form-group").show();
|
||||
$("#id_container").closest(".form-group").show();
|
||||
$("#id_action").closest(".form-group").show();
|
||||
$("#id_path_to_backup").closest(".form-group").show();
|
||||
$("#id_advanced_configuration").closest(".form-group").show();
|
||||
$("#id_mode").closest(".form-group").show();
|
||||
$("#id_tag").closest(".form-group").hide();
|
||||
}
|
||||
|
||||
function hideRestoreAdvancedConfigurationOptions() {
|
||||
hideEverything();
|
||||
$("#id_backup_name").closest(".form-group").show();
|
||||
$("#id_container").closest(".form-group").show();
|
||||
$("#id_action").closest(".form-group").show();
|
||||
$("#id_advanced_configuration").closest(".form-group").show();
|
||||
$("#id_restore_abs_path").closest(".form-group").show();
|
||||
$("#id_restore_from_host").closest(".form-group").show();
|
||||
$("#id_restore_from_date").closest(".form-group").show();
|
||||
}
|
||||
|
||||
function showRestoreAdvancedConfigurationOptions() {
|
||||
hideEverything();
|
||||
showRestoreOptions();
|
||||
}
|
||||
|
||||
hideEverything();
|
||||
|
||||
$("#id_action").change(function() {
|
||||
// Update the inputs according freezer action
|
||||
|
||||
if ($("#id_action").val() == 'backup') {
|
||||
hideEverything();
|
||||
showBackupOptions();
|
||||
hideAdvancedConfigurationOptions();
|
||||
$("#id_description").closest(".form-group").show();
|
||||
$("#id_client_id").closest(".form-group").show();
|
||||
}
|
||||
else if ($("#id_action").val() == 'restore') {
|
||||
hideEverything();
|
||||
showRestoreOptions();
|
||||
hideRestoreAdvancedConfigurationOptions();
|
||||
$("#id_description").closest(".form-group").show();
|
||||
$("#id_client_id").closest(".form-group").show();
|
||||
}
|
||||
else if ($("#id_action").val() == 'admin') {
|
||||
hideEverything();
|
||||
showAdminOptions();
|
||||
$("#id_client_id").closest(".form-group").show();
|
||||
$("#id_description").closest(".form-group").show();
|
||||
}
|
||||
else if ($("#id_action").val() == 'info') {
|
||||
hideEverything();
|
||||
}
|
||||
else {
|
||||
hideEverything();
|
||||
}
|
||||
});
|
||||
|
||||
$("#id_use_snapshot").click(function() {
|
||||
if ($("#id_use_snapshot").is(":checked")) {
|
||||
showSnapshotOptions();
|
||||
}
|
||||
else {
|
||||
hideSnapshotOptions();
|
||||
}
|
||||
});
|
||||
|
||||
$("#id_is_windows").click(function() {
|
||||
if ($("#id_use_snapshot").is(":checked")) {
|
||||
showSnapshotOptions();
|
||||
}
|
||||
else {
|
||||
hideSnapshotOptions();
|
||||
}
|
||||
});
|
||||
|
||||
$("#id_mode").change(function() {
|
||||
if ($("#id_action").val() == 'backup') {
|
||||
if ($("#id_mode").val() == 'fs') {
|
||||
hideEverything();
|
||||
showBackupOptions();
|
||||
showSnapshotOptions();
|
||||
$("#id_advanced_configuration").closest(".form-group").show();
|
||||
}
|
||||
else if ($("#id_mode").val() == 'mysql') {
|
||||
hideEverything();
|
||||
showBackupOptions();
|
||||
showSnapshotOptions();
|
||||
$("#id_mysql_conf").closest(".form-group").show();
|
||||
$("#id_sql_server_conf").closest(".form-group").hide();
|
||||
$("#id_advanced_configuration").closest(".form-group").show();
|
||||
}
|
||||
else if ($("#id_mode").val() == 'mssql') {
|
||||
hideEverything();
|
||||
showBackupOptions();
|
||||
showSnapshotOptions();
|
||||
$("#id_sql_server_conf").closest(".form-group").show();
|
||||
$("#id_mysql_conf").closest(".form-group").hide();
|
||||
$("#id_advanced_configuration").closest(".form-group").show();
|
||||
}
|
||||
else if ($("#id_mode").val() == 'cinder') {
|
||||
hideEverything();
|
||||
showCinderOptions();
|
||||
$("#id_cinder_vol_id").closest(".form-group").show().addClass("required");
|
||||
$("#id_advanced_configuration").closest(".form-group").show();
|
||||
}
|
||||
else if ($("#id_mode").val() == 'nova') {
|
||||
hideEverything();
|
||||
showNovaOptions();
|
||||
$("#id_nova_inst_id").closest(".form-group").show().addClass("required");
|
||||
$("#id_advanced_configuration").closest(".form-group").show();
|
||||
}
|
||||
else {
|
||||
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$("#id_upload").change(function() {
|
||||
if ($("#id_upload").is(":checked")) {
|
||||
$("#id_container").closest(".form-group").show().addClass("required");
|
||||
}
|
||||
else {
|
||||
$("#id_container").closest(".form-group").hide().removeClass("required");
|
||||
}
|
||||
});
|
||||
|
||||
$("#id_no_incremental").click(function() {
|
||||
if ($("#id_no_incremental").is(":checked")) {
|
||||
hideIncrementalOptions();
|
||||
}
|
||||
else {
|
||||
showIncrementalOptions();
|
||||
}
|
||||
});
|
||||
|
||||
$("#id_advanced_configuration").click(function() {
|
||||
if ($("#id_action").val() == 'backup') {
|
||||
if ($("#id_advanced_configuration").is(":checked")) {
|
||||
showAdvancedConfigurationOptions();
|
||||
$("#id_advanced_configuration").closest(".form-group").show();
|
||||
}
|
||||
else {
|
||||
hideAdvancedConfigurationOptions();
|
||||
}
|
||||
}
|
||||
else if ($("#id_action").val() == 'restore') {
|
||||
if ($("#id_advanced_configuration").is(":checked")) {
|
||||
showRestoreAdvancedConfigurationOptions();
|
||||
$("#id_advanced_configuration").closest(".form-group").show();
|
||||
}
|
||||
else {
|
||||
hideRestoreAdvancedConfigurationOptions();
|
||||
}
|
||||
}
|
||||
});
|
|
@ -10,13 +10,10 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from django.conf.urls import patterns
|
||||
from django.conf.urls import url
|
||||
|
||||
from horizon_web_ui.freezer_ui.overview import views
|
||||
|
||||
|
||||
urlpatterns = patterns(
|
||||
'',
|
||||
url(r'^$', views.IndexView.as_view(), name='index'),
|
||||
)
|
||||
def create_dict_action(**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}
|
Loading…
Reference in New Issue