1
# Copyright (c) 2010-2011 OpenStack, LLC.
3
# Licensed under the Apache License, Version 2.0 (the "License");
4
# you may not use this file except in compliance with the License.
5
# You may obtain a copy of the License at
7
# http://www.apache.org/licenses/LICENSE-2.0
9
# Unless required by applicable law or agreed to in writing, software
10
# distributed under the License is distributed on an "AS IS" BASIS,
11
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
13
# See the License for the specific language governing permissions and
14
# limitations under the License.
18
from paste.deploy import appconfig
22
from swift.account.server import DATADIR as account_server_data_dir
23
from swift.common.db import AccountBroker
24
from swift.common.utils import renamer, get_logger, readconf, mkdirs
25
from swift.common.constraints import check_mount
26
from swift.common.daemon import Daemon
29
class AccountStat(Daemon):
31
Extract storage stats from account databases on the account
35
def __init__(self, stats_conf):
36
super(AccountStat, self).__init__(stats_conf)
37
target_dir = stats_conf.get('log_dir', '/var/log/swift')
38
account_server_conf_loc = stats_conf.get('account_server_conf',
39
'/etc/swift/account-server.conf')
40
server_conf = appconfig('config:%s' % account_server_conf_loc,
41
name='account-server')
42
filename_format = stats_conf['source_filename_format']
43
if filename_format.count('*') > 1:
44
raise Exception('source filename format should have at max one *')
45
self.filename_format = filename_format
46
self.target_dir = target_dir
47
mkdirs(self.target_dir)
48
self.devices = server_conf.get('devices', '/srv/node')
49
self.mount_check = server_conf.get('mount_check', 'true').lower() in \
50
('true', 't', '1', 'on', 'yes', 'y')
52
get_logger(stats_conf, log_route='account-stats')
54
def run_once(self, *args, **kwargs):
55
self.logger.info(_("Gathering account stats"))
57
self.find_and_process()
59
_("Gathering account stats complete (%0.2f minutes)") %
60
((time.time() - start) / 60))
62
def find_and_process(self):
63
src_filename = time.strftime(self.filename_format)
64
working_dir = os.path.join(self.target_dir, '.stats_tmp')
65
shutil.rmtree(working_dir, ignore_errors=True)
67
tmp_filename = os.path.join(working_dir, src_filename)
68
hasher = hashlib.md5()
69
with open(tmp_filename, 'wb') as statfile:
70
# csv has the following columns:
71
# Account Name, Container Count, Object Count, Bytes Used
72
for device in os.listdir(self.devices):
73
if self.mount_check and not check_mount(self.devices, device):
75
_("Device %s is not mounted, skipping.") % device)
77
accounts = os.path.join(self.devices,
79
account_server_data_dir)
80
if not os.path.exists(accounts):
81
self.logger.debug(_("Path %s does not exist, skipping.") %
84
for root, dirs, files in os.walk(accounts, topdown=False):
85
for filename in files:
86
if filename.endswith('.db'):
87
db_path = os.path.join(root, filename)
88
broker = AccountBroker(db_path)
89
if not broker.is_deleted():
95
_junk, _junk) = broker.get_info()
96
line_data = '"%s",%d,%d,%d\n' % (
97
account_name, container_count,
98
object_count, bytes_used)
99
statfile.write(line_data)
100
hasher.update(line_data)
101
file_hash = hasher.hexdigest()
102
hash_index = src_filename.find('*')
104
# if there is no * in the target filename, the uploader probably
105
# won't work because we are crafting a filename that doesn't
107
src_filename = '_'.join([src_filename, file_hash])
109
parts = src_filename[:hash_index], src_filename[hash_index + 1:]
110
src_filename = ''.join([parts[0], file_hash, parts[1]])
111
renamer(tmp_filename, os.path.join(self.target_dir, src_filename))
112
shutil.rmtree(working_dir, ignore_errors=True)