diff --git a/bin/manila-backup b/bin/manila-backup deleted file mode 100755 index e28c32b12a..0000000000 --- a/bin/manila-backup +++ /dev/null @@ -1,50 +0,0 @@ -#!/usr/bin/env python - -# Copyright (C) 2012 Hewlett-Packard Development Company, L.P. -# All Rights Reserved. -# -# 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. - -"""Starter script for manila Volume Backup.""" - -import os -import sys - -import eventlet - -eventlet.monkey_patch() - -# If ../manila/__init__.py exists, add ../ to Python search path, so that -# it will override what happens to be installed in /usr/(local/)lib/python... -possible_topdir = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]), - os.pardir, - os.pardir)) -if os.path.exists(os.path.join(possible_topdir, 'manila', '__init__.py')): - sys.path.insert(0, possible_topdir) - -from manila.openstack.common import gettextutils -gettextutils.install('manila') - -from manila import flags -from manila.openstack.common import log as logging -from manila import service -from manila import utils - -if __name__ == '__main__': - flags.parse_args(sys.argv) - logging.setup("manila") - utils.monkey_patch() - launcher = service.ProcessLauncher() - server = service.Service.create(binary='manila-backup') - launcher.launch_server(server) - launcher.wait() diff --git a/bin/manila-manage b/bin/manila-manage index 3255bbba8a..db04dc197e 100755 --- a/bin/manila-manage +++ b/bin/manila-manage @@ -645,42 +645,6 @@ class GetLogCommands(object): print "No manila entries in syslog!" -class BackupCommands(object): - """Methods for managing backups.""" - - def list(self): - """List all backups (including ones in progress) and the host - on which the backup operation is running.""" - ctxt = context.get_admin_context() - backups = db.backup_get_all(ctxt) - - hdr = "%-32s\t%-32s\t%-32s\t%-24s\t%-24s\t%-12s\t%-12s\t%-12s\t%-12s" - print hdr % (_('ID'), - _('User ID'), - _('Project ID'), - _('Host'), - _('Name'), - _('Container'), - _('Status'), - _('Size'), - _('Object Count')) - - res = "%-32s\t%-32s\t%-32s\t%-24s\t%-24s\t%-12s\t%-12s\t%-12d\t%-12d" - for backup in backups: - object_count = 0 - if backup['object_count'] is not None: - object_count = backup['object_count'] - print res % (backup['id'], - backup['user_id'], - backup['project_id'], - backup['host'], - backup['display_name'], - backup['container'], - backup['status'], - backup['size'], - object_count) - - class ServiceCommands(object): """Methods for managing services.""" def list(self): diff --git a/bin/manila-volume b/bin/manila-volume deleted file mode 100755 index 66b7ada64b..0000000000 --- a/bin/manila-volume +++ /dev/null @@ -1,61 +0,0 @@ -#!/usr/bin/env python -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. -# All Rights Reserved. -# -# 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. - -"""Starter script for manila Volume.""" - -import eventlet -eventlet.monkey_patch() - -import os -import sys - -# If ../manila/__init__.py exists, add ../ to Python search path, so that -# it will override what happens to be installed in /usr/(local/)lib/python... -possible_topdir = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]), - os.pardir, - os.pardir)) -if os.path.exists(os.path.join(possible_topdir, 'manila', '__init__.py')): - sys.path.insert(0, possible_topdir) - -from manila.openstack.common import gettextutils -gettextutils.install('manila') - -from manila import flags -from manila.openstack.common import log as logging -from manila import service -from manila import utils - -FLAGS = flags.FLAGS - -if __name__ == '__main__': - flags.parse_args(sys.argv) - logging.setup("manila") - utils.monkey_patch() - launcher = service.ProcessLauncher() - if FLAGS.enabled_backends: - for backend in FLAGS.enabled_backends: - host = "%s@%s" % (FLAGS.host, backend) - server = service.Service.create( - host=host, - service_name=backend) - launcher.launch_server(server) - else: - server = service.Service.create(binary='manila-volume') - launcher.launch_server(server) - launcher.wait() diff --git a/bin/manila-volume-usage-audit b/bin/manila-volume-usage-audit deleted file mode 100755 index 5585d76f85..0000000000 --- a/bin/manila-volume-usage-audit +++ /dev/null @@ -1,101 +0,0 @@ -#!/usr/bin/env python -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright (c) 2011 OpenStack, LLC. -# All Rights Reserved. -# -# 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. - -"""Cron script to generate usage notifications for volumes existing during - the audit period. - - Together with the notifications generated by volumes - create/delete/resize, over that time period, this allows an external - system consuming usage notification feeds to calculate volume usage - for each tenant. - - Time periods are specified as 'hour', 'month', 'day' or 'year' - - hour = previous hour. If run at 9:07am, will generate usage for 8-9am. - month = previous month. If the script is run April 1, it will generate - usages for March 1 through March 31. - day = previous day. if run on July 4th, it generates usages for July 3rd. - year = previous year. If run on Jan 1, it generates usages for - Jan 1 through Dec 31 of the previous year. -""" - -import os -import sys -import traceback - -# If ../manila/__init__.py exists, add ../ to Python search path, so that -# it will override what happens to be installed in /usr/(local/)lib/python... -POSSIBLE_TOPDIR = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]), - os.pardir, - os.pardir)) -if os.path.exists(os.path.join(POSSIBLE_TOPDIR, 'manila', '__init__.py')): - sys.path.insert(0, POSSIBLE_TOPDIR) - -from manila.openstack.common import gettextutils -gettextutils.install('manila') - -from manila import context -from manila import db -from manila import flags -from manila.openstack.common import log as logging -from manila.openstack.common import rpc -from manila import utils -import manila.volume.utils - - -FLAGS = flags.FLAGS - -if __name__ == '__main__': - admin_context = context.get_admin_context() - flags.parse_args(sys.argv) - logging.setup("manila") - begin, end = utils.last_completed_audit_period() - print _("Starting volume usage audit") - msg = _("Creating usages for %(begin_period)s until %(end_period)s") - print (msg % {"begin_period": str(begin), "end_period": str(end)}) - - extra_info = { - 'audit_period_beginning': str(begin), - 'audit_period_ending': str(end), - } - - volumes = db.volume_get_active_by_window(admin_context, - begin, - end) - print _("Found %d volumes") % len(volumes) - for volume_ref in volumes: - try: - manila.volume.utils.notify_usage_exists( - admin_context, volume_ref) - except Exception, e: - print traceback.format_exc(e) - - snapshots = db.snapshot_get_active_by_window(admin_context, - begin, - end) - print _("Found %d snapshots") % len(snapshots) - for snapshot_ref in snapshots: - try: - manila.volume.utils.notify_about_snapshot_usage(admin_context, - snapshot_ref, - 'exists', - extra_info) - except Exception, e: - print traceback.fromat_exc(e) - - print _("Volume usage audit completed") diff --git a/manila/api/contrib/backups.py b/manila/api/contrib/backups.py deleted file mode 100644 index c7333f68fb..0000000000 --- a/manila/api/contrib/backups.py +++ /dev/null @@ -1,278 +0,0 @@ -# Copyright (C) 2012 Hewlett-Packard Development Company, L.P. -# All Rights Reserved. -# -# 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. - -"""The backups api.""" - -import webob -from webob import exc -from xml.dom import minidom - -from manila.api import common -from manila.api import extensions -from manila.api.openstack import wsgi -from manila.api.views import backups as backup_views -from manila.api import xmlutil -from manila import backup as backupAPI -from manila import exception -from manila import flags -from manila.openstack.common import log as logging - -FLAGS = flags.FLAGS -LOG = logging.getLogger(__name__) - - -def make_backup(elem): - elem.set('id') - elem.set('status') - elem.set('size') - elem.set('container') - elem.set('volume_id') - elem.set('object_count') - elem.set('availability_zone') - elem.set('created_at') - elem.set('name') - elem.set('description') - elem.set('fail_reason') - - -def make_backup_restore(elem): - elem.set('backup_id') - elem.set('volume_id') - - -class BackupTemplate(xmlutil.TemplateBuilder): - def construct(self): - root = xmlutil.TemplateElement('backup', selector='backup') - make_backup(root) - alias = Backups.alias - namespace = Backups.namespace - return xmlutil.MasterTemplate(root, 1, nsmap={alias: namespace}) - - -class BackupsTemplate(xmlutil.TemplateBuilder): - def construct(self): - root = xmlutil.TemplateElement('backups') - elem = xmlutil.SubTemplateElement(root, 'backup', selector='backups') - make_backup(elem) - alias = Backups.alias - namespace = Backups.namespace - return xmlutil.MasterTemplate(root, 1, nsmap={alias: namespace}) - - -class BackupRestoreTemplate(xmlutil.TemplateBuilder): - def construct(self): - root = xmlutil.TemplateElement('restore', selector='restore') - make_backup_restore(root) - alias = Backups.alias - namespace = Backups.namespace - return xmlutil.MasterTemplate(root, 1, nsmap={alias: namespace}) - - -class CreateDeserializer(wsgi.MetadataXMLDeserializer): - def default(self, string): - dom = minidom.parseString(string) - backup = self._extract_backup(dom) - return {'body': {'backup': backup}} - - def _extract_backup(self, node): - backup = {} - backup_node = self.find_first_child_named(node, 'backup') - - attributes = ['container', 'display_name', - 'display_description', 'volume_id'] - - for attr in attributes: - if backup_node.getAttribute(attr): - backup[attr] = backup_node.getAttribute(attr) - return backup - - -class RestoreDeserializer(wsgi.MetadataXMLDeserializer): - def default(self, string): - dom = minidom.parseString(string) - restore = self._extract_restore(dom) - return {'body': {'restore': restore}} - - def _extract_restore(self, node): - restore = {} - restore_node = self.find_first_child_named(node, 'restore') - if restore_node.getAttribute('volume_id'): - restore['volume_id'] = restore_node.getAttribute('volume_id') - return restore - - -class BackupsController(wsgi.Controller): - """The Backups API controller for the OpenStack API.""" - - _view_builder_class = backup_views.ViewBuilder - - def __init__(self): - self.backup_api = backupAPI.API() - super(BackupsController, self).__init__() - - @wsgi.serializers(xml=BackupTemplate) - def show(self, req, id): - """Return data about the given backup.""" - LOG.debug(_('show called for member %s'), id) - context = req.environ['manila.context'] - - try: - backup = self.backup_api.get(context, backup_id=id) - except exception.BackupNotFound as error: - raise exc.HTTPNotFound(explanation=unicode(error)) - - return self._view_builder.detail(req, backup) - - def delete(self, req, id): - """Delete a backup.""" - LOG.debug(_('delete called for member %s'), id) - context = req.environ['manila.context'] - - LOG.audit(_('Delete backup with id: %s'), id, context=context) - - try: - self.backup_api.delete(context, id) - except exception.BackupNotFound as error: - raise exc.HTTPNotFound(explanation=unicode(error)) - except exception.InvalidBackup as error: - raise exc.HTTPBadRequest(explanation=unicode(error)) - - return webob.Response(status_int=202) - - @wsgi.serializers(xml=BackupsTemplate) - def index(self, req): - """Returns a summary list of backups.""" - return self._get_backups(req, is_detail=False) - - @wsgi.serializers(xml=BackupsTemplate) - def detail(self, req): - """Returns a detailed list of backups.""" - return self._get_backups(req, is_detail=True) - - def _get_backups(self, req, is_detail): - """Returns a list of backups, transformed through view builder.""" - context = req.environ['manila.context'] - backups = self.backup_api.get_all(context) - limited_list = common.limited(backups, req) - - if is_detail: - backups = self._view_builder.detail_list(req, limited_list) - else: - backups = self._view_builder.summary_list(req, limited_list) - return backups - - # TODO(frankm): Add some checks here including - # - whether requested volume_id exists so we can return some errors - # immediately - # - maybe also do validation of swift container name - @wsgi.response(202) - @wsgi.serializers(xml=BackupTemplate) - @wsgi.deserializers(xml=CreateDeserializer) - def create(self, req, body): - """Create a new backup.""" - LOG.debug(_('Creating new backup %s'), body) - if not self.is_valid_body(body, 'backup'): - raise exc.HTTPBadRequest() - - context = req.environ['manila.context'] - - try: - backup = body['backup'] - volume_id = backup['volume_id'] - except KeyError: - msg = _("Incorrect request body format") - raise exc.HTTPBadRequest(explanation=msg) - container = backup.get('container', None) - name = backup.get('name', None) - description = backup.get('description', None) - - LOG.audit(_("Creating backup of volume %(volume_id)s in container" - " %(container)s"), locals(), context=context) - - try: - new_backup = self.backup_api.create(context, name, description, - volume_id, container) - except exception.InvalidVolume as error: - raise exc.HTTPBadRequest(explanation=unicode(error)) - except exception.VolumeNotFound as error: - raise exc.HTTPNotFound(explanation=unicode(error)) - - retval = self._view_builder.summary(req, dict(new_backup.iteritems())) - return retval - - @wsgi.response(202) - @wsgi.serializers(xml=BackupRestoreTemplate) - @wsgi.deserializers(xml=RestoreDeserializer) - def restore(self, req, id, body): - """Restore an existing backup to a volume.""" - backup_id = id - LOG.debug(_('Restoring backup %(backup_id)s (%(body)s)') % locals()) - if not self.is_valid_body(body, 'restore'): - raise exc.HTTPBadRequest() - - context = req.environ['manila.context'] - - try: - restore = body['restore'] - except KeyError: - msg = _("Incorrect request body format") - raise exc.HTTPBadRequest(explanation=msg) - volume_id = restore.get('volume_id', None) - - LOG.audit(_("Restoring backup %(backup_id)s to volume %(volume_id)s"), - locals(), context=context) - - try: - new_restore = self.backup_api.restore(context, - backup_id=backup_id, - volume_id=volume_id) - except exception.InvalidInput as error: - raise exc.HTTPBadRequest(explanation=unicode(error)) - except exception.InvalidVolume as error: - raise exc.HTTPBadRequest(explanation=unicode(error)) - except exception.InvalidBackup as error: - raise exc.HTTPBadRequest(explanation=unicode(error)) - except exception.BackupNotFound as error: - raise exc.HTTPNotFound(explanation=unicode(error)) - except exception.VolumeNotFound as error: - raise exc.HTTPNotFound(explanation=unicode(error)) - except exception.VolumeSizeExceedsAvailableQuota as error: - raise exc.HTTPRequestEntityTooLarge( - explanation=error.message, headers={'Retry-After': 0}) - except exception.VolumeLimitExceeded as error: - raise exc.HTTPRequestEntityTooLarge( - explanation=error.message, headers={'Retry-After': 0}) - - retval = self._view_builder.restore_summary( - req, dict(new_restore.iteritems())) - return retval - - -class Backups(extensions.ExtensionDescriptor): - """Backups support.""" - - name = 'Backups' - alias = 'backups' - namespace = 'http://docs.openstack.org/volume/ext/backups/api/v1' - updated = '2012-12-12T00:00:00+00:00' - - def get_resources(self): - resources = [] - res = extensions.ResourceExtension( - Backups.alias, BackupsController(), - collection_actions={'detail': 'GET'}, - member_actions={'restore': 'POST'}) - resources.append(res) - return resources diff --git a/manila/api/views/backups.py b/manila/api/views/backups.py deleted file mode 100644 index 4f57a941bb..0000000000 --- a/manila/api/views/backups.py +++ /dev/null @@ -1,90 +0,0 @@ -# Copyright (C) 2012 Hewlett-Packard Development Company, L.P. -# All Rights Reserved. -# -# 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 manila.api import common -from manila.openstack.common import log as logging - - -LOG = logging.getLogger(__name__) - - -class ViewBuilder(common.ViewBuilder): - """Model backup API responses as a python dictionary.""" - - _collection_name = "backups" - - def __init__(self): - """Initialize view builder.""" - super(ViewBuilder, self).__init__() - - def summary_list(self, request, backups): - """Show a list of backups without many details.""" - return self._list_view(self.summary, request, backups) - - def detail_list(self, request, backups): - """Detailed view of a list of backups .""" - return self._list_view(self.detail, request, backups) - - def summary(self, request, backup): - """Generic, non-detailed view of a backup.""" - return { - 'backup': { - 'id': backup['id'], - 'name': backup['display_name'], - 'links': self._get_links(request, - backup['id']), - }, - } - - def restore_summary(self, request, restore): - """Generic, non-detailed view of a restore.""" - return { - 'restore': { - 'backup_id': restore['backup_id'], - 'volume_id': restore['volume_id'], - }, - } - - def detail(self, request, backup): - """Detailed view of a single backup.""" - return { - 'backup': { - 'id': backup.get('id'), - 'status': backup.get('status'), - 'size': backup.get('size'), - 'object_count': backup.get('object_count'), - 'availability_zone': backup.get('availability_zone'), - 'container': backup.get('container'), - 'created_at': backup.get('created_at'), - 'name': backup.get('display_name'), - 'description': backup.get('display_description'), - 'fail_reason': backup.get('fail_reason'), - 'volume_id': backup.get('volume_id'), - 'links': self._get_links(request, backup['id']) - } - } - - def _list_view(self, func, request, backups): - """Provide a view for a list of backups.""" - backups_list = [func(request, backup)['backup'] for backup in backups] - backups_links = self._get_collection_links(request, - backups, - self._collection_name) - backups_dict = dict(backups=backups_list) - - if backups_links: - backups_dict['backups_links'] = backups_links - - return backups_dict diff --git a/manila/backup/__init__.py b/manila/backup/__init__.py deleted file mode 100644 index 8c557dc216..0000000000 --- a/manila/backup/__init__.py +++ /dev/null @@ -1,23 +0,0 @@ -# Copyright (C) 2012 Hewlett-Packard Development Company, L.P. -# All Rights Reserved. -# -# 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. - -# Importing full names to not pollute the namespace and cause possible -# collisions with use of 'from manila.backup import ' elsewhere. - -import manila.flags -import manila.openstack.common.importutils - -API = manila.openstack.common.importutils.import_class( - manila.flags.FLAGS.backup_api_class) diff --git a/manila/backup/api.py b/manila/backup/api.py deleted file mode 100644 index 333a98a39d..0000000000 --- a/manila/backup/api.py +++ /dev/null @@ -1,171 +0,0 @@ -# Copyright (C) 2012 Hewlett-Packard Development Company, L.P. -# All Rights Reserved. -# -# 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. - -""" -Handles all requests relating to the volume backups service. -""" - -from eventlet import greenthread - -from manila.backup import rpcapi as backup_rpcapi -from manila.db import base -from manila import exception -from manila import flags -from manila.openstack.common import log as logging -import manila.volume - - -FLAGS = flags.FLAGS - -LOG = logging.getLogger(__name__) - - -class API(base.Base): - """API for interacting with the volume backup manager.""" - - def __init__(self, db_driver=None): - self.backup_rpcapi = backup_rpcapi.BackupAPI() - self.volume_api = manila.volume.API() - super(API, self).__init__(db_driver) - - def get(self, context, backup_id): - rv = self.db.backup_get(context, backup_id) - return dict(rv.iteritems()) - - def delete(self, context, backup_id): - """ - Make the RPC call to delete a volume backup. - """ - backup = self.get(context, backup_id) - if backup['status'] not in ['available', 'error']: - msg = _('Backup status must be available or error') - raise exception.InvalidBackup(reason=msg) - - self.db.backup_update(context, backup_id, {'status': 'deleting'}) - self.backup_rpcapi.delete_backup(context, - backup['host'], - backup['id']) - - # TODO(moorehef): Add support for search_opts, discarded atm - def get_all(self, context, search_opts={}): - if context.is_admin: - backups = self.db.backup_get_all(context) - else: - backups = self.db.backup_get_all_by_project(context, - context.project_id) - - return backups - - def create(self, context, name, description, volume_id, - container, availability_zone=None): - """ - Make the RPC call to create a volume backup. - """ - volume = self.volume_api.get(context, volume_id) - if volume['status'] != "available": - msg = _('Volume to be backed up must be available') - raise exception.InvalidVolume(reason=msg) - self.db.volume_update(context, volume_id, {'status': 'backing-up'}) - - options = {'user_id': context.user_id, - 'project_id': context.project_id, - 'display_name': name, - 'display_description': description, - 'volume_id': volume_id, - 'status': 'creating', - 'container': container, - 'size': volume['size'], - # TODO(DuncanT): This will need de-managling once - # multi-backend lands - 'host': volume['host'], } - - backup = self.db.backup_create(context, options) - - #TODO(DuncanT): In future, when we have a generic local attach, - # this can go via the scheduler, which enables - # better load ballancing and isolation of services - self.backup_rpcapi.create_backup(context, - backup['host'], - backup['id'], - volume_id) - - return backup - - def restore(self, context, backup_id, volume_id=None): - """ - Make the RPC call to restore a volume backup. - """ - backup = self.get(context, backup_id) - if backup['status'] != 'available': - msg = _('Backup status must be available') - raise exception.InvalidBackup(reason=msg) - - size = backup['size'] - if size is None: - msg = _('Backup to be restored has invalid size') - raise exception.InvalidBackup(reason=msg) - - # Create a volume if none specified. If a volume is specified check - # it is large enough for the backup - if volume_id is None: - name = 'restore_backup_%s' % backup_id - description = 'auto-created_from_restore_from_swift' - - LOG.audit(_("Creating volume of %(size)s GB for restore of " - "backup %(backup_id)s"), locals(), context=context) - volume = self.volume_api.create(context, size, name, description) - volume_id = volume['id'] - - while True: - volume = self.volume_api.get(context, volume_id) - if volume['status'] != 'creating': - break - greenthread.sleep(1) - else: - volume = self.volume_api.get(context, volume_id) - volume_size = volume['size'] - if volume_size < size: - err = _('volume size %(volume_size)d is too small to restore ' - 'backup of size %(size)d.') % locals() - raise exception.InvalidVolume(reason=err) - - if volume['status'] != "available": - msg = _('Volume to be restored to must be available') - raise exception.InvalidVolume(reason=msg) - - LOG.debug('Checking backup size %s against volume size %s', - size, volume['size']) - if size > volume['size']: - msg = _('Volume to be restored to is smaller ' - 'than the backup to be restored') - raise exception.InvalidVolume(reason=msg) - - LOG.audit(_("Overwriting volume %(volume_id)s with restore of " - "backup %(backup_id)s"), locals(), context=context) - - # Setting the status here rather than setting at start and unrolling - # for each error condition, it should be a very small window - self.db.backup_update(context, backup_id, {'status': 'restoring'}) - self.db.volume_update(context, volume_id, {'status': - 'restoring-backup'}) - self.backup_rpcapi.restore_backup(context, - backup['host'], - backup['id'], - volume_id) - - d = {'backup_id': backup_id, - 'volume_id': volume_id, } - - return d diff --git a/manila/backup/manager.py b/manila/backup/manager.py deleted file mode 100755 index 5be06a3f5a..0000000000 --- a/manila/backup/manager.py +++ /dev/null @@ -1,264 +0,0 @@ -# Copyright (C) 2012 Hewlett-Packard Development Company, L.P. -# All Rights Reserved. -# -# 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. - -""" -Backup manager manages volume backups. - -Volume Backups are full copies of persistent volumes stored in Swift object -storage. They are usable without the original object being available. A -volume backup can be restored to the original volume it was created from or -any other available volume with a minimum size of the original volume. -Volume backups can be created, restored, deleted and listed. - -**Related Flags** - -:backup_topic: What :mod:`rpc` topic to listen to (default: - `manila-backup`). -:backup_manager: The module name of a class derived from - :class:`manager.Manager` (default: - :class:`manila.backup.manager.Manager`). - -""" - -from oslo.config import cfg - -from manila import context -from manila import exception -from manila import flags -from manila import manager -from manila.openstack.common import excutils -from manila.openstack.common import importutils -from manila.openstack.common import log as logging - -LOG = logging.getLogger(__name__) - -backup_manager_opts = [ - cfg.StrOpt('backup_service', - default='manila.backup.services.swift', - help='Service to use for backups.'), -] - -FLAGS = flags.FLAGS -FLAGS.register_opts(backup_manager_opts) - - -class BackupManager(manager.SchedulerDependentManager): - """Manages backup of block storage devices.""" - - RPC_API_VERSION = '1.0' - - def __init__(self, service_name=None, *args, **kwargs): - self.service = importutils.import_module(FLAGS.backup_service) - self.az = FLAGS.storage_availability_zone - self.volume_manager = importutils.import_object(FLAGS.volume_manager) - self.driver = self.volume_manager.driver - super(BackupManager, self).__init__(service_name='backup', - *args, **kwargs) - self.driver.db = self.db - - def init_host(self): - """Do any initialization that needs to be run if this is a - standalone service.""" - - ctxt = context.get_admin_context() - self.driver.do_setup(ctxt) - self.driver.check_for_setup_error() - - LOG.info(_("Cleaning up incomplete backup operations")) - volumes = self.db.volume_get_all_by_host(ctxt, self.host) - for volume in volumes: - if volume['status'] == 'backing-up': - LOG.info(_('Resetting volume %s to available ' - '(was backing-up)') % volume['id']) - self.volume_manager.detach_volume(ctxt, volume['id']) - if volume['status'] == 'restoring-backup': - LOG.info(_('Resetting volume %s to error_restoring ' - '(was restoring-backup)') % volume['id']) - self.volume_manager.detach_volume(ctxt, volume['id']) - self.db.volume_update(ctxt, volume['id'], - {'status': 'error_restoring'}) - - # TODO(smulcahy) implement full resume of backup and restore - # operations on restart (rather than simply resetting) - backups = self.db.backup_get_all_by_host(ctxt, self.host) - for backup in backups: - if backup['status'] == 'creating': - LOG.info(_('Resetting backup %s to error ' - '(was creating)') % backup['id']) - err = 'incomplete backup reset on manager restart' - self.db.backup_update(ctxt, backup['id'], {'status': 'error', - 'fail_reason': err}) - if backup['status'] == 'restoring': - LOG.info(_('Resetting backup %s to available ' - '(was restoring)') % backup['id']) - self.db.backup_update(ctxt, backup['id'], - {'status': 'available'}) - if backup['status'] == 'deleting': - LOG.info(_('Resuming delete on backup: %s') % backup['id']) - self.delete_backup(ctxt, backup['id']) - - def create_backup(self, context, backup_id): - """ - Create volume backups using configured backup service. - """ - backup = self.db.backup_get(context, backup_id) - volume_id = backup['volume_id'] - volume = self.db.volume_get(context, volume_id) - LOG.info(_('create_backup started, backup: %(backup_id)s for ' - 'volume: %(volume_id)s') % locals()) - self.db.backup_update(context, backup_id, {'host': self.host, - 'service': - FLAGS.backup_service}) - - expected_status = 'backing-up' - actual_status = volume['status'] - if actual_status != expected_status: - err = _('create_backup aborted, expected volume status ' - '%(expected_status)s but got %(actual_status)s') % locals() - self.db.backup_update(context, backup_id, {'status': 'error', - 'fail_reason': err}) - raise exception.InvalidVolume(reason=err) - - expected_status = 'creating' - actual_status = backup['status'] - if actual_status != expected_status: - err = _('create_backup aborted, expected backup status ' - '%(expected_status)s but got %(actual_status)s') % locals() - self.db.volume_update(context, volume_id, {'status': 'available'}) - self.db.backup_update(context, backup_id, {'status': 'error', - 'fail_reason': err}) - raise exception.InvalidBackup(reason=err) - - try: - backup_service = self.service.get_backup_service(context) - self.driver.backup_volume(context, backup, backup_service) - except Exception as err: - with excutils.save_and_reraise_exception(): - self.db.volume_update(context, volume_id, - {'status': 'available'}) - self.db.backup_update(context, backup_id, - {'status': 'error', - 'fail_reason': unicode(err)}) - - self.db.volume_update(context, volume_id, {'status': 'available'}) - self.db.backup_update(context, backup_id, {'status': 'available', - 'size': volume['size'], - 'availability_zone': - self.az}) - LOG.info(_('create_backup finished. backup: %s'), backup_id) - - def restore_backup(self, context, backup_id, volume_id): - """ - Restore volume backups from configured backup service. - """ - LOG.info(_('restore_backup started, restoring backup: %(backup_id)s' - ' to volume: %(volume_id)s') % locals()) - backup = self.db.backup_get(context, backup_id) - volume = self.db.volume_get(context, volume_id) - self.db.backup_update(context, backup_id, {'host': self.host}) - - expected_status = 'restoring-backup' - actual_status = volume['status'] - if actual_status != expected_status: - err = _('restore_backup aborted, expected volume status ' - '%(expected_status)s but got %(actual_status)s') % locals() - self.db.backup_update(context, backup_id, {'status': 'available'}) - raise exception.InvalidVolume(reason=err) - - expected_status = 'restoring' - actual_status = backup['status'] - if actual_status != expected_status: - err = _('restore_backup aborted, expected backup status ' - '%(expected_status)s but got %(actual_status)s') % locals() - self.db.backup_update(context, backup_id, {'status': 'error', - 'fail_reason': err}) - self.db.volume_update(context, volume_id, {'status': 'error'}) - raise exception.InvalidBackup(reason=err) - - if volume['size'] > backup['size']: - LOG.warn('volume: %s, size: %d is larger than backup: %s, ' - 'size: %d, continuing with restore', - volume['id'], volume['size'], - backup['id'], backup['size']) - - backup_service = backup['service'] - configured_service = FLAGS.backup_service - if backup_service != configured_service: - err = _('restore_backup aborted, the backup service currently' - ' configured [%(configured_service)s] is not the' - ' backup service that was used to create this' - ' backup [%(backup_service)s]') % locals() - self.db.backup_update(context, backup_id, {'status': 'available'}) - self.db.volume_update(context, volume_id, {'status': 'error'}) - raise exception.InvalidBackup(reason=err) - - try: - backup_service = self.service.get_backup_service(context) - self.driver.restore_backup(context, backup, volume, - backup_service) - except Exception as err: - with excutils.save_and_reraise_exception(): - self.db.volume_update(context, volume_id, - {'status': 'error_restoring'}) - self.db.backup_update(context, backup_id, - {'status': 'available'}) - - self.db.volume_update(context, volume_id, {'status': 'available'}) - self.db.backup_update(context, backup_id, {'status': 'available'}) - LOG.info(_('restore_backup finished, backup: %(backup_id)s restored' - ' to volume: %(volume_id)s') % locals()) - - def delete_backup(self, context, backup_id): - """ - Delete volume backup from configured backup service. - """ - backup = self.db.backup_get(context, backup_id) - LOG.info(_('delete_backup started, backup: %s'), backup_id) - self.db.backup_update(context, backup_id, {'host': self.host}) - - expected_status = 'deleting' - actual_status = backup['status'] - if actual_status != expected_status: - err = _('delete_backup aborted, expected backup status ' - '%(expected_status)s but got %(actual_status)s') % locals() - self.db.backup_update(context, backup_id, {'status': 'error', - 'fail_reason': err}) - raise exception.InvalidBackup(reason=err) - - backup_service = backup['service'] - if backup_service is not None: - configured_service = FLAGS.backup_service - if backup_service != configured_service: - err = _('delete_backup aborted, the backup service currently' - ' configured [%(configured_service)s] is not the' - ' backup service that was used to create this' - ' backup [%(backup_service)s]') % locals() - self.db.backup_update(context, backup_id, - {'status': 'error'}) - raise exception.InvalidBackup(reason=err) - - try: - backup_service = self.service.get_backup_service(context) - backup_service.delete(backup) - except Exception as err: - with excutils.save_and_reraise_exception(): - self.db.backup_update(context, backup_id, - {'status': 'error', - 'fail_reason': - unicode(err)}) - - context = context.elevated() - self.db.backup_destroy(context, backup_id) - LOG.info(_('delete_backup finished, backup %s deleted'), backup_id) diff --git a/manila/backup/rpcapi.py b/manila/backup/rpcapi.py deleted file mode 100644 index f503f9d8e6..0000000000 --- a/manila/backup/rpcapi.py +++ /dev/null @@ -1,73 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright (C) 2012 Hewlett-Packard Development Company, L.P. -# All Rights Reserved. -# -# 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. - -""" -Client side of the volume backup RPC API. -""" - -from manila import flags -from manila.openstack.common import log as logging -from manila.openstack.common import rpc -import manila.openstack.common.rpc.proxy - - -LOG = logging.getLogger(__name__) - -FLAGS = flags.FLAGS - - -class BackupAPI(manila.openstack.common.rpc.proxy.RpcProxy): - '''Client side of the volume rpc API. - - API version history: - - 1.0 - Initial version. - ''' - - BASE_RPC_API_VERSION = '1.0' - - def __init__(self): - super(BackupAPI, self).__init__( - topic=FLAGS.backup_topic, - default_version=self.BASE_RPC_API_VERSION) - - def create_backup(self, ctxt, host, backup_id, volume_id): - LOG.debug("create_backup in rpcapi backup_id %s", backup_id) - topic = rpc.queue_get_for(ctxt, self.topic, host) - LOG.debug("create queue topic=%s", topic) - self.cast(ctxt, - self.make_msg('create_backup', - backup_id=backup_id), - topic=topic) - - def restore_backup(self, ctxt, host, backup_id, volume_id): - LOG.debug("restore_backup in rpcapi backup_id %s", backup_id) - topic = rpc.queue_get_for(ctxt, self.topic, host) - LOG.debug("restore queue topic=%s", topic) - self.cast(ctxt, - self.make_msg('restore_backup', - backup_id=backup_id, - volume_id=volume_id), - topic=topic) - - def delete_backup(self, ctxt, host, backup_id): - LOG.debug("delete_backup rpcapi backup_id %s", backup_id) - topic = rpc.queue_get_for(ctxt, self.topic, host) - self.cast(ctxt, - self.make_msg('delete_backup', - backup_id=backup_id), - topic=topic) diff --git a/manila/backup/services/__init__.py b/manila/backup/services/__init__.py deleted file mode 100644 index f745a135ae..0000000000 --- a/manila/backup/services/__init__.py +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright (c) 2013 Hewlett-Packard Development Company, L.P. -# -# 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. diff --git a/manila/backup/services/swift.py b/manila/backup/services/swift.py deleted file mode 100644 index 5b625e0b2b..0000000000 --- a/manila/backup/services/swift.py +++ /dev/null @@ -1,384 +0,0 @@ -# Copyright (C) 2012 Hewlett-Packard Development Company, L.P. -# All Rights Reserved. -# -# 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. - -"""Implementation of a backup service that uses Swift as the backend - -**Related Flags** - -:backup_swift_url: The URL of the Swift endpoint (default: - localhost:8080). -:backup_swift_object_size: The size in bytes of the Swift objects used - for volume backups (default: 52428800). -:backup_swift_retry_attempts: The number of retries to make for Swift - operations (default: 10). -:backup_swift_retry_backoff: The backoff time in seconds between retrying - failed Swift operations (default: 10). -:backup_compression_algorithm: Compression algorithm to use for volume - backups. Supported options are: - None (to disable), zlib and bz2 (default: zlib) -""" - -import hashlib -import httplib -import json -import os -import socket -import StringIO - -import eventlet -from oslo.config import cfg - -from manila.db import base -from manila import exception -from manila import flags -from manila.openstack.common import log as logging -from manila.openstack.common import timeutils -from swiftclient import client as swift - -LOG = logging.getLogger(__name__) - -swiftbackup_service_opts = [ - cfg.StrOpt('backup_swift_url', - default='http://localhost:8080/v1/AUTH_', - help='The URL of the Swift endpoint'), - cfg.StrOpt('backup_swift_container', - default='volumebackups', - help='The default Swift container to use'), - cfg.IntOpt('backup_swift_object_size', - default=52428800, - help='The size in bytes of Swift backup objects'), - cfg.IntOpt('backup_swift_retry_attempts', - default=3, - help='The number of retries to make for Swift operations'), - cfg.IntOpt('backup_swift_retry_backoff', - default=2, - help='The backoff time in seconds between Swift retries'), - cfg.StrOpt('backup_compression_algorithm', - default='zlib', - help='Compression algorithm (None to disable)'), -] - -FLAGS = flags.FLAGS -FLAGS.register_opts(swiftbackup_service_opts) - - -class SwiftBackupService(base.Base): - """Provides backup, restore and delete of backup objects within Swift.""" - - SERVICE_VERSION = '1.0.0' - SERVICE_VERSION_MAPPING = {'1.0.0': '_restore_v1'} - - def _get_compressor(self, algorithm): - try: - if algorithm.lower() in ('none', 'off', 'no'): - return None - elif algorithm.lower() in ('zlib', 'gzip'): - import zlib as compressor - return compressor - elif algorithm.lower() in ('bz2', 'bzip2'): - import bz2 as compressor - return compressor - except ImportError: - pass - - err = _('unsupported compression algorithm: %s') % algorithm - raise ValueError(unicode(err)) - - def __init__(self, context, db_driver=None): - self.context = context - self.swift_url = '%s%s' % (FLAGS.backup_swift_url, - self.context.project_id) - self.az = FLAGS.storage_availability_zone - self.data_block_size_bytes = FLAGS.backup_swift_object_size - self.swift_attempts = FLAGS.backup_swift_retry_attempts - self.swift_backoff = FLAGS.backup_swift_retry_backoff - self.compressor = \ - self._get_compressor(FLAGS.backup_compression_algorithm) - self.conn = swift.Connection(None, None, None, - retries=self.swift_attempts, - preauthurl=self.swift_url, - preauthtoken=self.context.auth_token, - starting_backoff=self.swift_backoff) - super(SwiftBackupService, self).__init__(db_driver) - - def _check_container_exists(self, container): - LOG.debug(_('_check_container_exists: container: %s') % container) - try: - self.conn.head_container(container) - except swift.ClientException as error: - if error.http_status == httplib.NOT_FOUND: - LOG.debug(_('container %s does not exist') % container) - return False - else: - raise - else: - LOG.debug(_('container %s exists') % container) - return True - - def _create_container(self, context, backup): - backup_id = backup['id'] - container = backup['container'] - LOG.debug(_('_create_container started, container: %(container)s,' - 'backup: %(backup_id)s') % locals()) - if container is None: - container = FLAGS.backup_swift_container - self.db.backup_update(context, backup_id, {'container': container}) - if not self._check_container_exists(container): - self.conn.put_container(container) - return container - - def _generate_swift_object_name_prefix(self, backup): - az = 'az_%s' % self.az - backup_name = '%s_backup_%s' % (az, backup['id']) - volume = 'volume_%s' % (backup['volume_id']) - timestamp = timeutils.strtime(fmt="%Y%m%d%H%M%S") - prefix = volume + '/' + timestamp + '/' + backup_name - LOG.debug(_('_generate_swift_object_name_prefix: %s') % prefix) - return prefix - - def _generate_object_names(self, backup): - prefix = backup['service_metadata'] - swift_objects = self.conn.get_container(backup['container'], - prefix=prefix, - full_listing=True)[1] - swift_object_names = [] - for swift_object in swift_objects: - swift_object_names.append(swift_object['name']) - LOG.debug(_('generated object list: %s') % swift_object_names) - return swift_object_names - - def _metadata_filename(self, backup): - swift_object_name = backup['service_metadata'] - filename = '%s_metadata' % swift_object_name - return filename - - def _write_metadata(self, backup, volume_id, container, object_list): - filename = self._metadata_filename(backup) - LOG.debug(_('_write_metadata started, container name: %(container)s,' - ' metadata filename: %(filename)s') % locals()) - metadata = {} - metadata['version'] = self.SERVICE_VERSION - metadata['backup_id'] = backup['id'] - metadata['volume_id'] = volume_id - metadata['backup_name'] = backup['display_name'] - metadata['backup_description'] = backup['display_description'] - metadata['created_at'] = str(backup['created_at']) - metadata['objects'] = object_list - metadata_json = json.dumps(metadata, sort_keys=True, indent=2) - reader = StringIO.StringIO(metadata_json) - etag = self.conn.put_object(container, filename, reader) - md5 = hashlib.md5(metadata_json).hexdigest() - if etag != md5: - err = _('error writing metadata file to swift, MD5 of metadata' - ' file in swift [%(etag)s] is not the same as MD5 of ' - 'metadata file sent to swift [%(md5)s]') % locals() - raise exception.InvalidBackup(reason=err) - LOG.debug(_('_write_metadata finished')) - - def _read_metadata(self, backup): - container = backup['container'] - filename = self._metadata_filename(backup) - LOG.debug(_('_read_metadata started, container name: %(container)s, ' - 'metadata filename: %(filename)s') % locals()) - (resp, body) = self.conn.get_object(container, filename) - metadata = json.loads(body) - LOG.debug(_('_read_metadata finished (%s)') % metadata) - return metadata - - def backup(self, backup, volume_file): - """Backup the given volume to swift using the given backup metadata.""" - backup_id = backup['id'] - volume_id = backup['volume_id'] - volume = self.db.volume_get(self.context, volume_id) - - if volume['size'] <= 0: - err = _('volume size %d is invalid.') % volume['size'] - raise exception.InvalidVolume(reason=err) - - try: - container = self._create_container(self.context, backup) - except socket.error as err: - raise exception.SwiftConnectionFailed(reason=str(err)) - - object_prefix = self._generate_swift_object_name_prefix(backup) - backup['service_metadata'] = object_prefix - self.db.backup_update(self.context, backup_id, {'service_metadata': - object_prefix}) - volume_size_bytes = volume['size'] * 1024 * 1024 * 1024 - availability_zone = self.az - LOG.debug(_('starting backup of volume: %(volume_id)s to swift,' - ' volume size: %(volume_size_bytes)d, swift object names' - ' prefix %(object_prefix)s, availability zone:' - ' %(availability_zone)s') % locals()) - object_id = 1 - object_list = [] - while True: - data_block_size_bytes = self.data_block_size_bytes - object_name = '%s-%05d' % (object_prefix, object_id) - obj = {} - obj[object_name] = {} - obj[object_name]['offset'] = volume_file.tell() - data = volume_file.read(data_block_size_bytes) - obj[object_name]['length'] = len(data) - if data == '': - break - LOG.debug(_('reading chunk of data from volume')) - if self.compressor is not None: - algorithm = FLAGS.backup_compression_algorithm.lower() - obj[object_name]['compression'] = algorithm - data_size_bytes = len(data) - data = self.compressor.compress(data) - comp_size_bytes = len(data) - LOG.debug(_('compressed %(data_size_bytes)d bytes of data' - ' to %(comp_size_bytes)d bytes using ' - '%(algorithm)s') % locals()) - else: - LOG.debug(_('not compressing data')) - obj[object_name]['compression'] = 'none' - - reader = StringIO.StringIO(data) - LOG.debug(_('About to put_object')) - try: - etag = self.conn.put_object(container, object_name, reader) - except socket.error as err: - raise exception.SwiftConnectionFailed(reason=str(err)) - LOG.debug(_('swift MD5 for %(object_name)s: %(etag)s') % locals()) - md5 = hashlib.md5(data).hexdigest() - obj[object_name]['md5'] = md5 - LOG.debug(_('backup MD5 for %(object_name)s: %(md5)s') % locals()) - if etag != md5: - err = _('error writing object to swift, MD5 of object in ' - 'swift %(etag)s is not the same as MD5 of object sent ' - 'to swift %(md5)s') % locals() - raise exception.InvalidBackup(reason=err) - object_list.append(obj) - object_id += 1 - LOG.debug(_('Calling eventlet.sleep(0)')) - eventlet.sleep(0) - try: - self._write_metadata(backup, volume_id, container, object_list) - except socket.error as err: - raise exception.SwiftConnectionFailed(reason=str(err)) - self.db.backup_update(self.context, backup_id, {'object_count': - object_id}) - LOG.debug(_('backup %s finished.') % backup_id) - - def _restore_v1(self, backup, volume_id, metadata, volume_file): - """Restore a v1 swift volume backup from swift.""" - backup_id = backup['id'] - LOG.debug(_('v1 swift volume backup restore of %s started'), backup_id) - container = backup['container'] - metadata_objects = metadata['objects'] - metadata_object_names = [] - for metadata_object in metadata_objects: - metadata_object_names.extend(metadata_object.keys()) - LOG.debug(_('metadata_object_names = %s') % metadata_object_names) - prune_list = [self._metadata_filename(backup)] - swift_object_names = [swift_object_name for swift_object_name in - self._generate_object_names(backup) - if swift_object_name not in prune_list] - if sorted(swift_object_names) != sorted(metadata_object_names): - err = _('restore_backup aborted, actual swift object list in ' - 'swift does not match object list stored in metadata') - raise exception.InvalidBackup(reason=err) - - for metadata_object in metadata_objects: - object_name = metadata_object.keys()[0] - LOG.debug(_('restoring object from swift. backup: %(backup_id)s, ' - 'container: %(container)s, swift object name: ' - '%(object_name)s, volume: %(volume_id)s') % locals()) - try: - (resp, body) = self.conn.get_object(container, object_name) - except socket.error as err: - raise exception.SwiftConnectionFailed(reason=str(err)) - compression_algorithm = metadata_object[object_name]['compression'] - decompressor = self._get_compressor(compression_algorithm) - if decompressor is not None: - LOG.debug(_('decompressing data using %s algorithm') % - compression_algorithm) - decompressed = decompressor.decompress(body) - volume_file.write(decompressed) - else: - volume_file.write(body) - - # force flush every write to avoid long blocking write on close - volume_file.flush() - os.fsync(volume_file.fileno()) - # Restoring a backup to a volume can take some time. Yield so other - # threads can run, allowing for among other things the service - # status to be updated - eventlet.sleep(0) - LOG.debug(_('v1 swift volume backup restore of %s finished'), - backup_id) - - def restore(self, backup, volume_id, volume_file): - """Restore the given volume backup from swift.""" - backup_id = backup['id'] - container = backup['container'] - object_prefix = backup['service_metadata'] - LOG.debug(_('starting restore of backup %(object_prefix)s from swift' - ' container: %(container)s, to volume %(volume_id)s, ' - 'backup: %(backup_id)s') % locals()) - try: - metadata = self._read_metadata(backup) - except socket.error as err: - raise exception.SwiftConnectionFailed(reason=str(err)) - metadata_version = metadata['version'] - LOG.debug(_('Restoring swift backup version %s'), metadata_version) - try: - restore_func = getattr(self, self.SERVICE_VERSION_MAPPING.get( - metadata_version)) - except TypeError: - err = (_('No support to restore swift backup version %s') - % metadata_version) - raise exception.InvalidBackup(reason=err) - restore_func(backup, volume_id, metadata, volume_file) - LOG.debug(_('restore %(backup_id)s to %(volume_id)s finished.') % - locals()) - - def delete(self, backup): - """Delete the given backup from swift.""" - container = backup['container'] - LOG.debug('delete started, backup: %s, container: %s, prefix: %s', - backup['id'], container, backup['service_metadata']) - - if container is not None: - swift_object_names = [] - try: - swift_object_names = self._generate_object_names(backup) - except Exception: - LOG.warn(_('swift error while listing objects, continuing' - ' with delete')) - - for swift_object_name in swift_object_names: - try: - self.conn.delete_object(container, swift_object_name) - except socket.error as err: - raise exception.SwiftConnectionFailed(reason=str(err)) - except Exception: - LOG.warn(_('swift error while deleting object %s, ' - 'continuing with delete') % swift_object_name) - else: - LOG.debug(_('deleted swift object: %(swift_object_name)s' - ' in container: %(container)s') % locals()) - # Deleting a backup's objects from swift can take some time. - # Yield so other threads can run - eventlet.sleep(0) - - LOG.debug(_('delete %s finished') % backup['id']) - - -def get_backup_service(context): - return SwiftBackupService(context) diff --git a/manila/db/api.py b/manila/db/api.py index 3ee86af038..af2c6be55c 100644 --- a/manila/db/api.py +++ b/manila/db/api.py @@ -65,10 +65,7 @@ db_opts = [ 'names'), cfg.StrOpt('snapshot_name_template', default='snapshot-%s', - help='Template string to be used to generate snapshot names'), - cfg.StrOpt('backup_name_template', - default='backup-%s', - help='Template string to be used to generate backup names'), ] + help='Template string to be used to generate snapshot names'), ] FLAGS = flags.FLAGS FLAGS.register_opts(db_opts) diff --git a/manila/db/sqlalchemy/models.py b/manila/db/sqlalchemy/models.py index cebc0ae0de..6f7f8674a7 100644 --- a/manila/db/sqlalchemy/models.py +++ b/manila/db/sqlalchemy/models.py @@ -291,8 +291,7 @@ def register_models(): connection is lost and needs to be reestablished. """ from sqlalchemy import create_engine - models = (Backup, - Migration, + models = (Migration, Service, Share, ShareAccessMapping, diff --git a/manila/exception.py b/manila/exception.py index 3091cc23a2..d0334d8613 100644 --- a/manila/exception.py +++ b/manila/exception.py @@ -565,14 +565,6 @@ class ImageCopyFailure(Invalid): message = _("Failed to copy image to volume") -class BackupNotFound(NotFound): - message = _("Backup %(backup_id)s could not be found.") - - -class InvalidBackup(Invalid): - message = _("Invalid backup: %(reason)s") - - class InvalidShare(CinderException): message = _("Invalid share: %(reason)s") diff --git a/manila/flags.py b/manila/flags.py index 6bb1b89787..63eed2697e 100644 --- a/manila/flags.py +++ b/manila/flags.py @@ -134,12 +134,6 @@ global_opts = [ cfg.StrOpt('scheduler_topic', default='manila-scheduler', help='the topic scheduler nodes listen on'), - cfg.StrOpt('volume_topic', - default='manila-volume', - help='the topic volume nodes listen on'), - cfg.StrOpt('backup_topic', - default='manila-backup', - help='the topic volume backup nodes listen on'), cfg.StrOpt('share_topic', default='manila-share', help='the topic share nodes listen on'), @@ -188,9 +182,6 @@ global_opts = [ cfg.StrOpt('volume_manager', default='manila.volume.manager.VolumeManager', help='full class name for the Manager for volume'), - cfg.StrOpt('backup_manager', - default='manila.backup.manager.BackupManager', - help='full class name for the Manager for volume backup'), cfg.StrOpt('scheduler_manager', default='manila.scheduler.manager.SchedulerManager', help='full class name for the Manager for scheduler'), @@ -234,9 +225,6 @@ global_opts = [ cfg.StrOpt('volume_api_class', default='manila.volume.api.API', help='The full class name of the volume API class to use'), - cfg.StrOpt('backup_api_class', - default='manila.backup.api.API', - help='The full class name of the volume backup API class'), cfg.StrOpt('share_api_class', default='manila.share.api.API', help='The full class name of the share API class to use'), diff --git a/setup.cfg b/setup.cfg index db37d5457c..0b4f3f9bbe 100644 --- a/setup.cfg +++ b/setup.cfg @@ -28,15 +28,12 @@ packages = scripts = bin/manila-all bin/manila-api - bin/manila-backup bin/manila-clear-rabbit-queues bin/manila-manage bin/manila-rootwrap bin/manila-rpc-zmq-receiver bin/manila-scheduler bin/manila-share - bin/manila-volume - bin/manila-volume-usage-audit [entry_points] manila.scheduler.filters =