From 206a0ec9b75f869a8e1d138c4866bb0aeb075133 Mon Sep 17 00:00:00 2001 From: Memo Garcia Date: Wed, 1 Jul 2015 20:11:03 +0100 Subject: [PATCH] Horizon implementation for jobs api endpoint Implements: blueprint freezer-api-web-ui Change-Id: I8339d4b319f85964d33a2ab5d5c5e3669ca55f1c --- __init__.py | 1 - freezer_ui/actions/tables.py | 73 --- freezer_ui/actions/urls.py | 22 - freezer_ui/actions/views.py | 34 - freezer_ui/api/api.py | 317 ++++------ freezer_ui/backups/models.py | 3 - freezer_ui/backups/restore_workflow.py | 107 ---- freezer_ui/backups/tables.py | 111 ---- .../backups/templates/backups/detail.html | 17 - .../backups/templates/backups/index.html | 17 - .../backups/templates/backups/restore.html | 46 -- freezer_ui/backups/urls.py | 26 - freezer_ui/backups/views.py | 141 ----- freezer_ui/configurations/__init__.py | 1 - freezer_ui/configurations/browsers.py | 28 - freezer_ui/configurations/models.py | 3 - freezer_ui/configurations/panel.py | 24 - freezer_ui/configurations/tables.py | 161 ----- .../templates/configurations/browser.html | 11 - freezer_ui/configurations/utils.py | 40 -- freezer_ui/configurations/views.py | 94 --- .../configurations/workflows/configure.py | 315 ---------- freezer_ui/dashboard.py | 14 +- freezer_ui/{actions => jobs}/__init__.py | 0 .../{backups/panel.py => jobs/browsers.py} | 21 +- freezer_ui/{actions => jobs}/models.py | 0 freezer_ui/{actions => jobs}/panel.py | 9 +- freezer_ui/jobs/tables.py | 226 +++++++ freezer_ui/jobs/templates/jobs/_action.html | 7 + freezer_ui/jobs/templates/jobs/_actions.html | 7 + freezer_ui/jobs/templates/jobs/_advanced.html | 7 + .../jobs/templates/jobs/_scheduling.html | 21 + freezer_ui/jobs/templates/jobs/_snapshot.html | 7 + .../jobs}/_workflow_step_update_members.html | 0 .../templates/jobs/browser.html} | 6 +- .../jobs/templates/jobs/scheduling.html | 21 + freezer_ui/jobs/templates/jobs/snapshot.html | 7 + freezer_ui/{configurations => jobs}/urls.py | 17 +- freezer_ui/jobs/views.py | 133 ++++ .../{backups => jobs/workflows}/__init__.py | 0 freezer_ui/jobs/workflows/action.py | 580 ++++++++++++++++++ freezer_ui/jobs/workflows/configure.py | 231 +++++++ freezer_ui/overview/__init__.py | 1 - freezer_ui/overview/models.py | 3 - freezer_ui/overview/panel.py | 24 - .../overview/templates/overview/overview.html | 112 ---- freezer_ui/overview/views.py | 21 - .../freezer/js/freezer.actions.action.js | 131 ++++ .../freezer/js/freezer.actions.advanced.js | 21 + .../freezer/js/freezer.actions.snapshot.js | 81 +++ .../js/freezer.jobs.actions.action.js} | 0 freezer_ui/static/freezer/js/freezer.jobs.js | 361 +++++++++++ freezer_ui/{overview/urls.py => utils.py} | 15 +- 53 files changed, 2016 insertions(+), 1660 deletions(-) delete mode 100644 freezer_ui/actions/tables.py delete mode 100644 freezer_ui/actions/urls.py delete mode 100644 freezer_ui/actions/views.py delete mode 100644 freezer_ui/backups/models.py delete mode 100644 freezer_ui/backups/restore_workflow.py delete mode 100644 freezer_ui/backups/tables.py delete mode 100644 freezer_ui/backups/templates/backups/detail.html delete mode 100644 freezer_ui/backups/templates/backups/index.html delete mode 100644 freezer_ui/backups/templates/backups/restore.html delete mode 100644 freezer_ui/backups/urls.py delete mode 100644 freezer_ui/backups/views.py delete mode 100644 freezer_ui/configurations/__init__.py delete mode 100644 freezer_ui/configurations/browsers.py delete mode 100644 freezer_ui/configurations/models.py delete mode 100644 freezer_ui/configurations/panel.py delete mode 100644 freezer_ui/configurations/tables.py delete mode 100644 freezer_ui/configurations/templates/configurations/browser.html delete mode 100644 freezer_ui/configurations/utils.py delete mode 100644 freezer_ui/configurations/views.py delete mode 100644 freezer_ui/configurations/workflows/configure.py rename freezer_ui/{actions => jobs}/__init__.py (100%) rename freezer_ui/{backups/panel.py => jobs/browsers.py} (57%) rename freezer_ui/{actions => jobs}/models.py (100%) rename freezer_ui/{actions => jobs}/panel.py (81%) create mode 100644 freezer_ui/jobs/tables.py create mode 100644 freezer_ui/jobs/templates/jobs/_action.html create mode 100644 freezer_ui/jobs/templates/jobs/_actions.html create mode 100644 freezer_ui/jobs/templates/jobs/_advanced.html create mode 100644 freezer_ui/jobs/templates/jobs/_scheduling.html create mode 100644 freezer_ui/jobs/templates/jobs/_snapshot.html rename freezer_ui/{configurations/templates/configurations => jobs/templates/jobs}/_workflow_step_update_members.html (100%) rename freezer_ui/{actions/templates/actions/index.html => jobs/templates/jobs/browser.html} (59%) create mode 100644 freezer_ui/jobs/templates/jobs/scheduling.html create mode 100644 freezer_ui/jobs/templates/jobs/snapshot.html rename freezer_ui/{configurations => jobs}/urls.py (67%) create mode 100644 freezer_ui/jobs/views.py rename freezer_ui/{backups => jobs/workflows}/__init__.py (100%) create mode 100644 freezer_ui/jobs/workflows/action.py create mode 100644 freezer_ui/jobs/workflows/configure.py delete mode 100644 freezer_ui/overview/__init__.py delete mode 100644 freezer_ui/overview/models.py delete mode 100644 freezer_ui/overview/panel.py delete mode 100644 freezer_ui/overview/templates/overview/overview.html delete mode 100644 freezer_ui/overview/views.py create mode 100644 freezer_ui/static/freezer/js/freezer.actions.action.js create mode 100644 freezer_ui/static/freezer/js/freezer.actions.advanced.js create mode 100644 freezer_ui/static/freezer/js/freezer.actions.snapshot.js rename freezer_ui/{configurations/workflows/__init__.py => static/freezer/js/freezer.jobs.actions.action.js} (100%) create mode 100644 freezer_ui/static/freezer/js/freezer.jobs.js rename freezer_ui/{overview/urls.py => utils.py} (71%) diff --git a/__init__.py b/__init__.py index d95ce28..e69de29 100644 --- a/__init__.py +++ b/__init__.py @@ -1 +0,0 @@ -__author__ = 'jonas' diff --git a/freezer_ui/actions/tables.py b/freezer_ui/actions/tables.py deleted file mode 100644 index 6015ba7..0000000 --- a/freezer_ui/actions/tables.py +++ /dev/null @@ -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") diff --git a/freezer_ui/actions/urls.py b/freezer_ui/actions/urls.py deleted file mode 100644 index 07913af..0000000 --- a/freezer_ui/actions/urls.py +++ /dev/null @@ -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'), -) diff --git a/freezer_ui/actions/views.py b/freezer_ui/actions/views.py deleted file mode 100644 index 5158086..0000000 --- a/freezer_ui/actions/views.py +++ /dev/null @@ -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 diff --git a/freezer_ui/api/api.py b/freezer_ui/api/api.py index c664448..4047e5a 100644 --- a/freezer_ui/api/api.py +++ b/freezer_ui/api/api.py @@ -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 diff --git a/freezer_ui/backups/models.py b/freezer_ui/backups/models.py deleted file mode 100644 index 1b3d5f9..0000000 --- a/freezer_ui/backups/models.py +++ /dev/null @@ -1,3 +0,0 @@ -""" -Stub file to work around django bug: https://code.djangoproject.com/ticket/7198 -""" diff --git a/freezer_ui/backups/restore_workflow.py b/freezer_ui/backups/restore_workflow.py deleted file mode 100644 index 43f56ec..0000000 --- a/freezer_ui/backups/restore_workflow.py +++ /dev/null @@ -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 diff --git a/freezer_ui/backups/tables.py b/freezer_ui/backups/tables.py deleted file mode 100644 index 7dfc068..0000000 --- a/freezer_ui/backups/tables.py +++ /dev/null @@ -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 = '' - - level_txt = "Level: {} ({} backup) out of {}".format( - backup.level, "Full" if backup.level == 0 else "Incremental", - backup.max_level) - result.append( - '{}'.format( - level_txt, backup.level)) - - if backup.encrypted: - result.append( - '') - else: - result.append(placeholder) - - if int(backup.total_broken_links) > 0: - result.append( - ''.format(backup.total_broken_links)) - else: - result.append(placeholder) - - if backup.excluded_files: - result.append( - ''.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 diff --git a/freezer_ui/backups/templates/backups/detail.html b/freezer_ui/backups/templates/backups/detail.html deleted file mode 100644 index 1a5e67a..0000000 --- a/freezer_ui/backups/templates/backups/detail.html +++ /dev/null @@ -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 %} -
-
-
{{ data }}
-
-
- -{% endblock %} diff --git a/freezer_ui/backups/templates/backups/index.html b/freezer_ui/backups/templates/backups/index.html deleted file mode 100644 index 4987dcd..0000000 --- a/freezer_ui/backups/templates/backups/index.html +++ /dev/null @@ -1,17 +0,0 @@ -{% extends 'base.html' %} - -{% block css %} - {% include "_stylesheets.html" %} - -{% 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 %} \ No newline at end of file diff --git a/freezer_ui/backups/templates/backups/restore.html b/freezer_ui/backups/templates/backups/restore.html deleted file mode 100644 index 0e744f7..0000000 --- a/freezer_ui/backups/templates/backups/restore.html +++ /dev/null @@ -1,46 +0,0 @@ - -
-
- - - - - - - - - - - - - - - - - - - - - - - -
-
- -
-
HostnameDescription
- - {$ client['client']['client_id'] $}{$ client['client']['description'] $}
- {$ filtered.length ? filtered.length : 0 $} out of {$ clients.length $} displayed. Use the filter field to limit the number of results. -
-
-
- {% include "horizon/common/_form_fields.html" %} - {{ table.render }} -
-
- {{ step.get_help_text }} -
-
diff --git a/freezer_ui/backups/urls.py b/freezer_ui/backups/urls.py deleted file mode 100644 index 971b4e1..0000000 --- a/freezer_ui/backups/urls.py +++ /dev/null @@ -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[^/]*)$', views.DetailView.as_view(), name='detail'), - url(r'^restore/(?P.*)$', - views.RestoreView.as_view(), - name='restore'), -) diff --git a/freezer_ui/backups/views.py b/freezer_ui/backups/views.py deleted file mode 100644 index a781eef..0000000 --- a/freezer_ui/backups/views.py +++ /dev/null @@ -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 diff --git a/freezer_ui/configurations/__init__.py b/freezer_ui/configurations/__init__.py deleted file mode 100644 index d95ce28..0000000 --- a/freezer_ui/configurations/__init__.py +++ /dev/null @@ -1 +0,0 @@ -__author__ = 'jonas' diff --git a/freezer_ui/configurations/browsers.py b/freezer_ui/configurations/browsers.py deleted file mode 100644 index 5a0e4dd..0000000 --- a/freezer_ui/configurations/browsers.py +++ /dev/null @@ -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" diff --git a/freezer_ui/configurations/models.py b/freezer_ui/configurations/models.py deleted file mode 100644 index 1b3d5f9..0000000 --- a/freezer_ui/configurations/models.py +++ /dev/null @@ -1,3 +0,0 @@ -""" -Stub file to work around django bug: https://code.djangoproject.com/ticket/7198 -""" diff --git a/freezer_ui/configurations/panel.py b/freezer_ui/configurations/panel.py deleted file mode 100644 index 082e3fd..0000000 --- a/freezer_ui/configurations/panel.py +++ /dev/null @@ -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) diff --git a/freezer_ui/configurations/tables.py b/freezer_ui/configurations/tables.py deleted file mode 100644 index 3351ee4..0000000 --- a/freezer_ui/configurations/tables.py +++ /dev/null @@ -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( - ' {}'.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 diff --git a/freezer_ui/configurations/templates/configurations/browser.html b/freezer_ui/configurations/templates/configurations/browser.html deleted file mode 100644 index a5e354f..0000000 --- a/freezer_ui/configurations/templates/configurations/browser.html +++ /dev/null @@ -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 %} diff --git a/freezer_ui/configurations/utils.py b/freezer_ui/configurations/utils.py deleted file mode 100644 index f62b258..0000000 --- a/freezer_ui/configurations/utils.py +++ /dev/null @@ -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 diff --git a/freezer_ui/configurations/views.py b/freezer_ui/configurations/views.py deleted file mode 100644 index 1dd2026..0000000 --- a/freezer_ui/configurations/views.py +++ /dev/null @@ -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 diff --git a/freezer_ui/configurations/workflows/configure.py b/freezer_ui/configurations/workflows/configure.py deleted file mode 100644 index 8fbc061..0000000 --- a/freezer_ui/configurations/workflows/configure.py +++ /dev/null @@ -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 diff --git a/freezer_ui/dashboard.py b/freezer_ui/dashboard.py index b90b42a..d0604e2 100644 --- a/freezer_ui/dashboard.py +++ b/freezer_ui/dashboard.py @@ -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) diff --git a/freezer_ui/actions/__init__.py b/freezer_ui/jobs/__init__.py similarity index 100% rename from freezer_ui/actions/__init__.py rename to freezer_ui/jobs/__init__.py diff --git a/freezer_ui/backups/panel.py b/freezer_ui/jobs/browsers.py similarity index 57% rename from freezer_ui/backups/panel.py rename to freezer_ui/jobs/browsers.py index 919a73c..17d7a12 100644 --- a/freezer_ui/backups/panel.py +++ b/freezer_ui/jobs/browsers.py @@ -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" diff --git a/freezer_ui/actions/models.py b/freezer_ui/jobs/models.py similarity index 100% rename from freezer_ui/actions/models.py rename to freezer_ui/jobs/models.py diff --git a/freezer_ui/actions/panel.py b/freezer_ui/jobs/panel.py similarity index 81% rename from freezer_ui/actions/panel.py rename to freezer_ui/jobs/panel.py index 7e35a68..43a1886 100644 --- a/freezer_ui/actions/panel.py +++ b/freezer_ui/jobs/panel.py @@ -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) diff --git a/freezer_ui/jobs/tables.py b/freezer_ui/jobs/tables.py new file mode 100644 index 0000000..dd3a27d --- /dev/null +++ b/freezer_ui/jobs/tables.py @@ -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( + ' {}'.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 diff --git a/freezer_ui/jobs/templates/jobs/_action.html b/freezer_ui/jobs/templates/jobs/_action.html new file mode 100644 index 0000000..84a70e8 --- /dev/null +++ b/freezer_ui/jobs/templates/jobs/_action.html @@ -0,0 +1,7 @@ +{% load i18n horizon humanize %} + +{% block help_message %} +{% endblock %} + + + diff --git a/freezer_ui/jobs/templates/jobs/_actions.html b/freezer_ui/jobs/templates/jobs/_actions.html new file mode 100644 index 0000000..311bf4e --- /dev/null +++ b/freezer_ui/jobs/templates/jobs/_actions.html @@ -0,0 +1,7 @@ +{% load i18n horizon humanize %} + +{% block help_message %} +{% endblock %} + + + diff --git a/freezer_ui/jobs/templates/jobs/_advanced.html b/freezer_ui/jobs/templates/jobs/_advanced.html new file mode 100644 index 0000000..b514a7f --- /dev/null +++ b/freezer_ui/jobs/templates/jobs/_advanced.html @@ -0,0 +1,7 @@ +{% load i18n horizon humanize %} + +{% block help_message %} +{% endblock %} + + + \ No newline at end of file diff --git a/freezer_ui/jobs/templates/jobs/_scheduling.html b/freezer_ui/jobs/templates/jobs/_scheduling.html new file mode 100644 index 0000000..b2f15cc --- /dev/null +++ b/freezer_ui/jobs/templates/jobs/_scheduling.html @@ -0,0 +1,21 @@ +{% load i18n horizon humanize %} + +{% block help_message %} +

{% blocktrans %}Start and End Date Time{% endblocktrans %}

+

{% blocktrans %}Set a start date and time to execute jobs in ISO format:{% endblocktrans %}

+
    +
  • YYYY-MM-DDThh:mm:ss
  • +
+ +

{% blocktrans %}Interval{% endblocktrans %}

+

{% blocktrans %}Set the interval in the following format:{% endblocktrans %}

+
    +
  • continuous
  • +
  • N weeks
  • +
  • N days
  • +
  • N hours
  • +
  • N minutes
  • +
  • N seconds
  • +
+ +{% endblock %} \ No newline at end of file diff --git a/freezer_ui/jobs/templates/jobs/_snapshot.html b/freezer_ui/jobs/templates/jobs/_snapshot.html new file mode 100644 index 0000000..36c0a71 --- /dev/null +++ b/freezer_ui/jobs/templates/jobs/_snapshot.html @@ -0,0 +1,7 @@ +{% load i18n horizon humanize %} + +{% block help_message %} +{% endblock %} + + + \ No newline at end of file diff --git a/freezer_ui/configurations/templates/configurations/_workflow_step_update_members.html b/freezer_ui/jobs/templates/jobs/_workflow_step_update_members.html similarity index 100% rename from freezer_ui/configurations/templates/configurations/_workflow_step_update_members.html rename to freezer_ui/jobs/templates/jobs/_workflow_step_update_members.html diff --git a/freezer_ui/actions/templates/actions/index.html b/freezer_ui/jobs/templates/jobs/browser.html similarity index 59% rename from freezer_ui/actions/templates/actions/index.html rename to freezer_ui/jobs/templates/jobs/browser.html index 46ac305..2f4b58e 100644 --- a/freezer_ui/actions/templates/actions/index.html +++ b/freezer_ui/jobs/templates/jobs/browser.html @@ -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 %} diff --git a/freezer_ui/jobs/templates/jobs/scheduling.html b/freezer_ui/jobs/templates/jobs/scheduling.html new file mode 100644 index 0000000..b2f15cc --- /dev/null +++ b/freezer_ui/jobs/templates/jobs/scheduling.html @@ -0,0 +1,21 @@ +{% load i18n horizon humanize %} + +{% block help_message %} +

{% blocktrans %}Start and End Date Time{% endblocktrans %}

+

{% blocktrans %}Set a start date and time to execute jobs in ISO format:{% endblocktrans %}

+
    +
  • YYYY-MM-DDThh:mm:ss
  • +
+ +

{% blocktrans %}Interval{% endblocktrans %}

+

{% blocktrans %}Set the interval in the following format:{% endblocktrans %}

+
    +
  • continuous
  • +
  • N weeks
  • +
  • N days
  • +
  • N hours
  • +
  • N minutes
  • +
  • N seconds
  • +
+ +{% endblock %} \ No newline at end of file diff --git a/freezer_ui/jobs/templates/jobs/snapshot.html b/freezer_ui/jobs/templates/jobs/snapshot.html new file mode 100644 index 0000000..36c0a71 --- /dev/null +++ b/freezer_ui/jobs/templates/jobs/snapshot.html @@ -0,0 +1,7 @@ +{% load i18n horizon humanize %} + +{% block help_message %} +{% endblock %} + + + \ No newline at end of file diff --git a/freezer_ui/configurations/urls.py b/freezer_ui/jobs/urls.py similarity index 67% rename from freezer_ui/configurations/urls.py rename to freezer_ui/jobs/urls.py index d73d025..6840fbd 100644 --- a/freezer_ui/configurations/urls.py +++ b/freezer_ui/jobs/urls.py @@ -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[^/]+)?$', - views.BackupConfigsView.as_view(), + url(r'^(?P[^/]+)?$', + views.JobsView.as_view(), name='index'), url(r'^create/$', - views.ConfigureWorkflowView.as_view(), + views.JobWorkflowView.as_view(), name='create'), - url(r'^configure/(?P[^/]+)?$', - views.ConfigureWorkflowView.as_view(), + url(r'^create_action/(?P[^/]+)?$', + views.ActionWorkflowView.as_view(), + name='create_action'), + + url(r'^configure/(?P[^/]+)?$', + views.JobWorkflowView.as_view(), name='configure'), ) diff --git a/freezer_ui/jobs/views.py b/freezer_ui/jobs/views.py new file mode 100644 index 0000000..ba18e49 --- /dev/null +++ b/freezer_ui/jobs/views.py @@ -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 + diff --git a/freezer_ui/backups/__init__.py b/freezer_ui/jobs/workflows/__init__.py similarity index 100% rename from freezer_ui/backups/__init__.py rename to freezer_ui/jobs/workflows/__init__.py diff --git a/freezer_ui/jobs/workflows/action.py b/freezer_ui/jobs/workflows/action.py new file mode 100644 index 0000000..b2d837b --- /dev/null +++ b/freezer_ui/jobs/workflows/action.py @@ -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 diff --git a/freezer_ui/jobs/workflows/configure.py b/freezer_ui/jobs/workflows/configure.py new file mode 100644 index 0000000..192354d --- /dev/null +++ b/freezer_ui/jobs/workflows/configure.py @@ -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 diff --git a/freezer_ui/overview/__init__.py b/freezer_ui/overview/__init__.py deleted file mode 100644 index d95ce28..0000000 --- a/freezer_ui/overview/__init__.py +++ /dev/null @@ -1 +0,0 @@ -__author__ = 'jonas' diff --git a/freezer_ui/overview/models.py b/freezer_ui/overview/models.py deleted file mode 100644 index 1b3d5f9..0000000 --- a/freezer_ui/overview/models.py +++ /dev/null @@ -1,3 +0,0 @@ -""" -Stub file to work around django bug: https://code.djangoproject.com/ticket/7198 -""" diff --git a/freezer_ui/overview/panel.py b/freezer_ui/overview/panel.py deleted file mode 100644 index 8139f85..0000000 --- a/freezer_ui/overview/panel.py +++ /dev/null @@ -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) diff --git a/freezer_ui/overview/templates/overview/overview.html b/freezer_ui/overview/templates/overview/overview.html deleted file mode 100644 index 09fdfa7..0000000 --- a/freezer_ui/overview/templates/overview/overview.html +++ /dev/null @@ -1,112 +0,0 @@ -{% extends 'base.html' %} -{% load i18n %} -{% block title %}{% trans "Backup Restore DR Overview" %}{% endblock %} - -{% block css %} - {% include "_stylesheets.html" %} - -{% endblock %} - - - -{% block main %} - -
-

{% trans "Overview" %}

- -
-
- {% trans "Nodes without backup in the last week" %}
- 18 of 50 -
-
- -
-
- {% trans "Total space used for backups" %}
- 150gb of 200gb -
-
- -
-
- {% trans "Storage price per month" %}
- 60 dls -
-
-
- - -
-

{% trans "Backup Summary" %}

- -
-
- {% trans "Backups older than 1 week" %}
- 60 of 120 -
-
- -
-
- {% trans "Average backup size" %}
- 7gb for 18 backups -
-
- -
-
- {% trans "Average backup time" %}
- 18 min -
-
- -
- -
-

{% trans "Restore Summary" %}

- -
-
- {% trans "Average restore time" %}
- 15 min -
-
- -
-
- {% trans "Restore ratio success to failure" %}
- 100% success -
-
-
- - -
-

{% trans "OS Summary" %}

- -
- -
-
- - - - - - - - -{% endblock %} \ No newline at end of file diff --git a/freezer_ui/overview/views.py b/freezer_ui/overview/views.py deleted file mode 100644 index a974306..0000000 --- a/freezer_ui/overview/views.py +++ /dev/null @@ -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") diff --git a/freezer_ui/static/freezer/js/freezer.actions.action.js b/freezer_ui/static/freezer/js/freezer.actions.action.js new file mode 100644 index 0000000..24a0286 --- /dev/null +++ b/freezer_ui/static/freezer/js/freezer.actions.action.js @@ -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 { + + } + } +}); + diff --git a/freezer_ui/static/freezer/js/freezer.actions.advanced.js b/freezer_ui/static/freezer/js/freezer.actions.advanced.js new file mode 100644 index 0000000..41773a6 --- /dev/null +++ b/freezer_ui/static/freezer/js/freezer.actions.advanced.js @@ -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(); \ No newline at end of file diff --git a/freezer_ui/static/freezer/js/freezer.actions.snapshot.js b/freezer_ui/static/freezer/js/freezer.actions.snapshot.js new file mode 100644 index 0000000..7e1adb9 --- /dev/null +++ b/freezer_ui/static/freezer/js/freezer.actions.snapshot.js @@ -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(); + } +}); \ No newline at end of file diff --git a/freezer_ui/configurations/workflows/__init__.py b/freezer_ui/static/freezer/js/freezer.jobs.actions.action.js similarity index 100% rename from freezer_ui/configurations/workflows/__init__.py rename to freezer_ui/static/freezer/js/freezer.jobs.actions.action.js diff --git a/freezer_ui/static/freezer/js/freezer.jobs.js b/freezer_ui/static/freezer/js/freezer.jobs.js new file mode 100644 index 0000000..0a4d59e --- /dev/null +++ b/freezer_ui/static/freezer/js/freezer.jobs.js @@ -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(); + } + } +}); \ No newline at end of file diff --git a/freezer_ui/overview/urls.py b/freezer_ui/utils.py similarity index 71% rename from freezer_ui/overview/urls.py rename to freezer_ui/utils.py index 5427446..745ad88 100644 --- a/freezer_ui/overview/urls.py +++ b/freezer_ui/utils.py @@ -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}