manila/manila/tests/db_utils.py
Goutham Pacha Ravi 94e04c8de2 Data Replication: Ensure Snapshots across replicas
When taking a snapshot of a replicated share,  users would
expect the snapshot to be available across the replicas.
Currently, Manila assumes that drivers will take snapshots
along with the share data.

Different backends implement replication differently. Many
of them assume that replicas are 'in_sync' with the primary
if they have been synchronized within a particular window
of time (sometimes referred to as 'recovery point objective').
Since snapshots are point in time actions on a share, drivers
must ensure that the replication of a snapshot begins as soon
as it is available on the primary/'active' instance.

To ensure that snapshots are indeed available across replicas:
1) Track snapshots across replicas as different
    snapshot instances.
2) Make an 'aggregate_status' property to convey the
    availability of a snapshot across all the active/in_sync
    replicas of a share.
3) Provide all the information necessary for drivers to:
    - Create a snapshot on the primary and send it across
       to all replicas
    - Delete a snapshot on the primary and ensure that it is
       deleted on the replicas as well.
4) Set snapshot instance to 'error' if it was in any transitional
    state when its replica was 'promoted' to become an 'active'
    instance.
5) Pass down snapshot instances during create, delete and update
replica driver calls.
6) Update replica interfaces to pass in snapshot instance data. Always
include 'available' snapshots in the update_replica call to ensure
that drivers confirm their presence on the replica in order to report
that the replica is 'in_sync'.

Also, grab the share_type afresh from parent share/snapshot
to allow 'replication_key' to be copied to the share model during
share creation.

Rename SnapshotNotFound to SnapshotResourceNotFound to
signify a missing snapshot object on backends.

APIImpact
The 'status' attribute of the snapshot is now the 'aggregate_status'
of the instances of the snapshot. No key changes have been made
in the JSON response. However, API actions will be gated against
'aggregate_status' of the snapshot instances when more than one
instance exists.

Closes-Bug: #1546303
Closes-Bug: #1551064

Change-Id: I63203df00904d209e9e92eda7c79206e5ef8d611
2016-03-18 20:29:11 -04:00

210 lines
6.2 KiB
Python

# Copyright 2015 Mirantis Inc.
# 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.
import copy
from manila.common import constants
from manila import context
from manila import db
def _create_db_row(method, default_values, custom_values):
override_defaults = custom_values.pop('override_defaults', None)
if override_defaults:
default_values = custom_values
else:
default_values.update(copy.deepcopy(custom_values))
return method(context.get_admin_context(), default_values)
def create_consistency_group(**kwargs):
"""Create a consistency group object."""
cg = {
'share_network_id': None,
'share_server_id': None,
'user_id': 'fake',
'project_id': 'fake',
'status': constants.STATUS_CREATING,
'host': 'fake_host'
}
return _create_db_row(db.consistency_group_create, cg, kwargs)
def create_cgsnapshot(cg_id, **kwargs):
"""Create a cgsnapshot object."""
snapshot = {
'consistency_group_id': cg_id,
'user_id': 'fake',
'project_id': 'fake',
'status': constants.STATUS_CREATING,
}
return _create_db_row(db.cgsnapshot_create, snapshot, kwargs)
def create_cgsnapshot_member(cgsnapshot_id, **kwargs):
"""Create a cgsnapshot member object."""
member = {
'share_proto': "NFS",
'size': 0,
'share_id': None,
'share_instance_id': None,
'user_id': 'fake',
'project_id': 'fake',
'status': 'creating',
'cgsnapshot_id': cgsnapshot_id,
}
return _create_db_row(db.cgsnapshot_member_create, member, kwargs)
def create_share(**kwargs):
"""Create a share object."""
share = {
'share_proto': "NFS",
'size': 0,
'snapshot_id': None,
'share_network_id': None,
'share_server_id': None,
'user_id': 'fake',
'project_id': 'fake',
'metadata': {'fake_key': 'fake_value'},
'availability_zone': 'fake_availability_zone',
'status': constants.STATUS_CREATING,
'host': 'fake_host'
}
return _create_db_row(db.share_create, share, kwargs)
def create_share_instance(**kwargs):
"""Create a share instance object."""
instance = {
'host': 'fake',
'status': constants.STATUS_CREATING,
}
instance.update(kwargs)
return db.share_instance_create(context.get_admin_context(),
kwargs.pop('share_id'), kwargs)
def create_share_replica(**kwargs):
"""Create a share replica object."""
replica = {
'host': 'fake',
'status': constants.STATUS_CREATING,
}
replica.update(kwargs)
if 'share_id' not in kwargs:
share = create_share()
kwargs['share_id'] = share['id']
return db.share_instance_create(context.get_admin_context(),
kwargs.pop('share_id'), kwargs)
def create_snapshot(**kwargs):
"""Create a snapshot object."""
with_share = kwargs.pop('with_share', False)
share = None
if with_share:
share = create_share(status=constants.STATUS_AVAILABLE,
size=kwargs.get('size', 0))
snapshot = {
'share_proto': "NFS",
'size': 0,
'share_id': share['id'] if with_share else None,
'user_id': 'fake',
'project_id': 'fake',
'status': 'creating',
'provider_location': 'fake',
}
snapshot.update(kwargs)
return db.share_snapshot_create(context.get_admin_context(), snapshot)
def create_snapshot_instance(snapshot_id, **kwargs):
"""Create a share snapshot instance object."""
snapshot_instance = {
'provider_location': 'fake_provider_location',
'progress': '0%',
'status': constants.STATUS_CREATING,
}
snapshot_instance.update(kwargs)
return db.share_snapshot_instance_create(
context.get_admin_context(), snapshot_id, snapshot_instance)
def create_access(**kwargs):
"""Create a access rule object."""
access = {
'access_type': 'fake_type',
'access_to': 'fake_IP',
'share_id': None,
}
return _create_db_row(db.share_access_create, access, kwargs)
def create_share_server(**kwargs):
"""Create a share server object."""
backend_details = kwargs.pop('backend_details', {})
srv = {
'host': 'host1',
'share_network_id': 'fake_srv_id',
'status': constants.STATUS_ACTIVE
}
share_srv = _create_db_row(db.share_server_create, srv, kwargs)
if backend_details:
db.share_server_backend_details_set(
context.get_admin_context(), share_srv['id'], backend_details)
return db.share_server_get(context.get_admin_context(),
share_srv['id'])
def create_share_network(**kwargs):
"""Create a share network object."""
net = {
'user_id': 'fake',
'project_id': 'fake',
'neutron_net_id': 'fake-neutron-net',
'neutron_subnet_id': 'fake-neutron-subnet',
'status': 'new',
'network_type': 'vlan',
'segmentation_id': 1000,
'cidr': '10.0.0.0/24',
'ip_version': 4,
'name': 'whatever',
'description': 'fake description',
}
return _create_db_row(db.share_network_create, net, kwargs)
def create_security_service(**kwargs):
share_network_id = kwargs.pop('share_network_id', None)
service = {
'type': "FAKE",
'project_id': 'fake-project-id',
}
service_ref = _create_db_row(db.security_service_create, service, kwargs)
if share_network_id:
db.share_network_add_security_service(context.get_admin_context(),
share_network_id,
service_ref['id'])
return service_ref