trove backup

introduction

Code Reading

backup

api:create_backup —-> taskmanager:create_backup —-> self.guest.create_backup —-> rabbitmq —-> guestagent:mysql_manager:create_backup —-> innobackupex —-> swift put

taskmanager 中的 create_backup

trove/taskmanager/manager.py

1
2
3
4
class Manager(periodic_task.PeriodicTasks):
def create_backup(self, context, backup_info, instance_id):
instance_tasks = models.BuiltInstanceTasks.load(context, instance_id)
instance_tasks.create_backup(backup_info)

trove/taskmanager/models.py

1
2
3
4
class BuiltInstanceTasks(BuiltInstance, NotifyMixin, ConfigurationMixin):
def create_backup(self, backup_info):
LOG.info(_(“Initiating backup for instance %s.”) % self.id)
self.guest.create_backup(backup_info)

这里的 self.guest trove/instance/models.py

1
2
3
class BaseInstance(SimpleInstance):
def get_guest(self):
return create_guest_client(self.context, self.db_info.id)

trove/common/remote.py

1
2
3
4
5
6
7
8
9
create_guest_client = import_class(CONF.remote_guest_client)

def guest_client(context, id, manager=None):
from trove.guestagent.api import API
if manager:
clazz = strategy.load_guestagent_strategy(manager).guest_client_class
else:
clazz = API
return clazz(context, id)

所以 guest 对应的是 API 实例, cast()发送消息。guestagent 接受到消息

guestagent 中的 createbackup

trove/guestagent/datastore/mysql/manager.py

1
2
3
class Manager(periodic_task.PeriodicTasks):
def create_backup(self, context, backup_info):
backup.backup(context, backup_info)

trove/guestagent/backup/backupagent.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# innobackupex 或 mysqldump
RUNNER = get_backup_strategy(STRATEGY, BACKUP_NAMESPACE)
# 额外的参数,如使用 innobackupex,可以有–user os_admin –socket /var/lib/mysql/mysql.sock
EXTRA_OPTS = CONF.backup_runner_options.get(STRATEGY, ‘’)

class BackupAgent(object):

def stream_backup_to_storage(self, backup_info, runner, storage,
parent_metadata={}, extra_opts=EXTRA_OPTS):

backup_id = backup_info[‘id’]
ctxt = trove_context.TroveContext(
user=CONF.nova_proxy_admin_user,
auth_token=CONF.nova_proxy_admin_pass)
conductor = conductor_api.API(ctxt)

# Store the size of the filesystem before the backup.
mount_point = CONFIG_MANAGER.mount_point
stats = get_filesystem_volume_stats(mount_point)
backup_state = {
‘backup_id’: backup_id,
‘size’: stats.get(‘used’, 0.0),
‘state’: BackupState.BUILDING,
}
conductor.update_backup(CONF.guest_id,
sent=timeutils.float_utcnow(),
backup_state)
LOG.debug(“Updated state for %s to %s.”, backup_id, backup_state)

with runner(filename=backup_id, extra_opts=extra_opts,
parent_metadata) as bkup:
try:
LOG.debug(“Starting backup %s.”, backup_id)
success, note, checksum, location = storage.save(
bkup.manifest,
bkup)

backup_state.update({
‘checksum’: checksum,
‘location’: location,
‘note’: note,
‘success’: success,
‘backup_type’: bkup.backup_type,
})
. . .

if not success:
raise BackupError(note)

meta = bkup.metadata()
meta[‘datastore’] = backup_info[‘datastore’]
meta[‘datastore_version’] = backup_info[
‘datastore_version’]
storage.save_metadata(location, meta)

backup_state.update({‘state’: BackupState.COMPLETED})

return meta
. . .

def execute_backup(self, context, backup_info,
runner=RUNNER, extra_opts=EXTRA_OPTS):


LOG.debug(“Running backup %(id)s.”, backup_info)
storage = get_storage_strategy(
CONF.storage_strategy,
CONF.storage_namespace)(context)

#是否是 incremental backup
. . .
self.stream_backup_to_storage(backup_info, runner, storage,
parent_metadata, extra_opts)

RUNNER 对应的是 trove.guestagent.strategies.backup.mysql_impl:InnoBackupEx/InnoBackupExIncremental , 使用 stream backup

1
2
3
4
5
6
7
class BackupRunner(Strategy):

def enter(self):
“””Start up the process.”””
self._run_pre_backup()
self.run()
return self

storage 对应的是 trove.guestagent.strategies.storage.swift:SwiftStorage , 从 stream read 出来 使用 swift put 上传.

radosgw’s swift api

rgw 集成 keystone 部分配置

1
2
3
rgw keystone url = http://192.168.6.125:5000
# 若不配置该项,需要配置 rgw_keystone_admin_user rgw_keystone_admin_password rgw_keystone_admin_tenant 三项,去请求 url 得到 admin token (rgw_swift.cc:244)
rgw keystone admin token = 85cc6b3914c042fe8be37032ff03fa49

rgw 接受到 swift 请求之后处理

1
ret = handler->authorize();

请求携带 token,对 token 进行验证

1
bool RGWSwift::verify_swift_token(RGWRados store, req_state s)

validatetoken 返回详细信息和其 tenant、user、role 等信息, 对应请求的 keystone API /v2.0/tokens/{tokenId}

1
validate_keystone_token(store, s->os_auth_token, &info, s->user);
1
parse_keystone_token_response(token, bl, info, t)

parse 之后的 info

1
{status = 200, auth_groups = “”, user = “a400d920a63642d4913e8f2f3afda408”, display_name = “demo”, ttl = 0}
1
update_user_info(store, info, rgw_user);

对应关系:

1
2
info user –> keystone tenant_id –> rgw uid
info display_name –> keystone tenant_name –> rgw display_name

那使用 rgw swift 来作 trove backup, 但是很遗憾。trove 配置项 backup_swift_container = database_backups , 创建 backup 时为 tenant 创建一个 container 用来存放 backup,但是 rgw 的 bucket_name 是全局的,swift 中 containername 是 tenant 下的。当我使用第二个 tenant 去创建 backups 时,会先创建database<sub>backups</sub>的 bucket,而这个 bucket 已被占用,BuceketAlreadyExist

或许可以使用 database_backups_$(tenant_id) 的 bucket 来存储,这种方式在 juno 版是可以(trove juno 使 swift DLO 方式上传)trove master 使用了 swift SLO 的方式,目前 rgw swift 不支持。so, openstack or implement s3 in trove.