~ttx/swift/release-1.4.2

« back to all changes in this revision

Viewing changes to swift/stats/db_stats_collector.py

  • Committer: Tarmac
  • Author(s): gholt, FUJITA Tomonori, John Dickinson, David Goetz, John Dickinson, Joe Arnold, Scott Simpson, joe at cloudscaling, Thierry Carrez
  • Date: 2011-07-26 09:08:37 UTC
  • mfrom: (305.1.1 milestone-proposed)
  • Revision ID: tarmac-20110726090837-fwlvja8dnk7nkppw
Merge 1.4.2 development from trunk (rev331)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (c) 2010-2011 OpenStack, LLC.
2
 
#
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
6
 
#
7
 
#    http://www.apache.org/licenses/LICENSE-2.0
8
 
#
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
12
 
# implied.
13
 
# See the License for the specific language governing permissions and
14
 
# limitations under the License.
15
 
 
16
 
import os
17
 
import time
18
 
from paste.deploy import appconfig
19
 
import shutil
20
 
import hashlib
21
 
import urllib
22
 
 
23
 
from swift.account.server import DATADIR as account_server_data_dir
24
 
from swift.container.server import DATADIR as container_server_data_dir
25
 
from swift.common.db import AccountBroker, ContainerBroker
26
 
from swift.common.utils import renamer, get_logger, readconf, mkdirs, \
27
 
    TRUE_VALUES, remove_file
28
 
from swift.common.constraints import check_mount
29
 
from swift.common.daemon import Daemon
30
 
 
31
 
 
32
 
class DatabaseStatsCollector(Daemon):
33
 
    """
34
 
    Extract storage stats from account databases on the account
35
 
    storage nodes
36
 
 
37
 
    Any subclasses must define the function get_data.
38
 
    """
39
 
 
40
 
    def __init__(self, stats_conf, stats_type, data_dir, filename_format):
41
 
        super(DatabaseStatsCollector, self).__init__(stats_conf)
42
 
        self.stats_type = stats_type
43
 
        self.data_dir = data_dir
44
 
        self.filename_format = filename_format
45
 
        self.devices = stats_conf.get('devices', '/srv/node')
46
 
        self.mount_check = stats_conf.get('mount_check',
47
 
                                           'true').lower() in TRUE_VALUES
48
 
        self.target_dir = stats_conf.get('log_dir', '/var/log/swift')
49
 
        mkdirs(self.target_dir)
50
 
        self.logger = get_logger(stats_conf,
51
 
                                 log_route='%s-stats' % stats_type)
52
 
 
53
 
    def run_once(self, *args, **kwargs):
54
 
        self.logger.info(_("Gathering %s stats" % self.stats_type))
55
 
        start = time.time()
56
 
        self.find_and_process()
57
 
        self.logger.info(_("Gathering %s stats complete (%0.2f minutes)") %
58
 
                         (self.stats_type, (time.time() - start) / 60))
59
 
 
60
 
    def get_data(self):
61
 
        raise NotImplementedError('Subclasses must override')
62
 
 
63
 
    def get_header(self):
64
 
        raise NotImplementedError('Subclasses must override')
65
 
 
66
 
    def find_and_process(self):
67
 
        src_filename = time.strftime(self.filename_format)
68
 
        working_dir = os.path.join(self.target_dir,
69
 
                                   '.%-stats_tmp' % self.stats_type)
70
 
        shutil.rmtree(working_dir, ignore_errors=True)
71
 
        mkdirs(working_dir)
72
 
        tmp_filename = os.path.join(working_dir, src_filename)
73
 
        hasher = hashlib.md5()
74
 
        try:
75
 
            with open(tmp_filename, 'wb') as statfile:
76
 
                statfile.write(self.get_header())
77
 
                for device in os.listdir(self.devices):
78
 
                    if self.mount_check and not check_mount(self.devices,
79
 
                                                            device):
80
 
                        self.logger.error(
81
 
                            _("Device %s is not mounted, skipping.") % device)
82
 
                        continue
83
 
                    db_dir = os.path.join(self.devices, device, self.data_dir)
84
 
                    if not os.path.exists(db_dir):
85
 
                        self.logger.debug(
86
 
                            _("Path %s does not exist, skipping.") % db_dir)
87
 
                        continue
88
 
                    for root, dirs, files in os.walk(db_dir, topdown=False):
89
 
                        for filename in files:
90
 
                            if filename.endswith('.db'):
91
 
                                db_path = os.path.join(root, filename)
92
 
                                line_data = self.get_data(db_path)
93
 
                                if line_data:
94
 
                                    statfile.write(line_data)
95
 
                                    hasher.update(line_data)
96
 
 
97
 
            src_filename += hasher.hexdigest()
98
 
            renamer(tmp_filename, os.path.join(self.target_dir, src_filename))
99
 
        finally:
100
 
            shutil.rmtree(working_dir, ignore_errors=True)
101
 
 
102
 
 
103
 
class AccountStatsCollector(DatabaseStatsCollector):
104
 
    """
105
 
    Extract storage stats from account databases on the account
106
 
    storage nodes
107
 
    """
108
 
 
109
 
    def __init__(self, stats_conf):
110
 
        super(AccountStatsCollector, self).__init__(stats_conf, 'account',
111
 
                                                    account_server_data_dir,
112
 
                                                    'stats-%Y%m%d%H_')
113
 
 
114
 
    def get_data(self, db_path):
115
 
        """
116
 
        Data for generated csv has the following columns:
117
 
        Account Hash, Container Count, Object Count, Bytes Used
118
 
        """
119
 
        line_data = None
120
 
        broker = AccountBroker(db_path)
121
 
        if not broker.is_deleted():
122
 
            info = broker.get_info()
123
 
            line_data = '"%s",%d,%d,%d\n' % (info['account'],
124
 
                                             info['container_count'],
125
 
                                             info['object_count'],
126
 
                                             info['bytes_used'])
127
 
        return line_data
128
 
 
129
 
    def get_header(self):
130
 
        return ''
131
 
 
132
 
 
133
 
class ContainerStatsCollector(DatabaseStatsCollector):
134
 
    """
135
 
    Extract storage stats from container databases on the container
136
 
    storage nodes
137
 
    """
138
 
 
139
 
    def __init__(self, stats_conf):
140
 
        super(ContainerStatsCollector, self).__init__(stats_conf, 'container',
141
 
                                                   container_server_data_dir,
142
 
                                                   'container-stats-%Y%m%d%H_')
143
 
        # webob calls title on all the header keys
144
 
        self.metadata_keys = ['X-Container-Meta-%s' % mkey.strip().title()
145
 
                for mkey in stats_conf.get('metadata_keys', '').split(',')
146
 
                if mkey.strip()]
147
 
 
148
 
    def get_header(self):
149
 
        header = 'Account Hash,Container Name,Object Count,Bytes Used'
150
 
        if self.metadata_keys:
151
 
            xtra_headers = ','.join(self.metadata_keys)
152
 
            header += ',%s' % xtra_headers
153
 
        header += '\n'
154
 
        return header
155
 
 
156
 
    def get_data(self, db_path):
157
 
        """
158
 
        Data for generated csv has the following columns:
159
 
        Account Hash, Container Name, Object Count, Bytes Used
160
 
        This will just collect whether or not the metadata is set
161
 
        using a 1 or ''.
162
 
        """
163
 
        line_data = None
164
 
        broker = ContainerBroker(db_path)
165
 
        if not broker.is_deleted():
166
 
            info = broker.get_info(include_metadata=bool(self.metadata_keys))
167
 
            encoded_container_name = urllib.quote(info['container'])
168
 
            line_data = '"%s","%s",%d,%d' % (
169
 
                info['account'], encoded_container_name,
170
 
                info['object_count'], info['bytes_used'])
171
 
            if self.metadata_keys:
172
 
                metadata_results = ','.join(
173
 
                    [info['metadata'].get(mkey) and '1' or ''
174
 
                     for mkey in self.metadata_keys])
175
 
                line_data += ',%s' % metadata_results
176
 
            line_data += '\n'
177
 
        return line_data