~notmyname/swift/deslogging

« back to all changes in this revision

Viewing changes to swift/stats/db_stats_collector.py

  • Committer: Tarmac
  • Author(s): David Goetz, Jay Payne
  • Date: 2011-05-18 15:48:17 UTC
  • mfrom: (286.3.22 containerstat)
  • Revision ID: tarmac-20110518154817-n03d5aig142496q2
Adding container stats collector, unit tests, and refactoring some of the stats code.  There will have to be changes to both the swift and rackswift conf files before this can be released.  Please DO NOT approve this branch for merge until glange's stats stuff is all ready to go.  gracias.

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 Exception('Not Implemented')
 
62
 
 
63
    def find_and_process(self):
 
64
        src_filename = time.strftime(self.filename_format)
 
65
        working_dir = os.path.join(self.target_dir,
 
66
                                   '.%-stats_tmp' % self.stats_type)
 
67
        shutil.rmtree(working_dir, ignore_errors=True)
 
68
        mkdirs(working_dir)
 
69
        tmp_filename = os.path.join(working_dir, src_filename)
 
70
        hasher = hashlib.md5()
 
71
        try:
 
72
            with open(tmp_filename, 'wb') as statfile:
 
73
                for device in os.listdir(self.devices):
 
74
                    if self.mount_check and not check_mount(self.devices,
 
75
                                                            device):
 
76
                        self.logger.error(
 
77
                            _("Device %s is not mounted, skipping.") % device)
 
78
                        continue
 
79
                    db_dir = os.path.join(self.devices, device, self.data_dir)
 
80
                    if not os.path.exists(db_dir):
 
81
                        self.logger.debug(
 
82
                            _("Path %s does not exist, skipping.") % db_dir)
 
83
                        continue
 
84
                    for root, dirs, files in os.walk(db_dir, topdown=False):
 
85
                        for filename in files:
 
86
                            if filename.endswith('.db'):
 
87
                                db_path = os.path.join(root, filename)
 
88
                                line_data = self.get_data(db_path)
 
89
                                if line_data:
 
90
                                    statfile.write(line_data)
 
91
                                    hasher.update(line_data)
 
92
 
 
93
            src_filename += hasher.hexdigest()
 
94
            renamer(tmp_filename, os.path.join(self.target_dir, src_filename))
 
95
        finally:
 
96
            shutil.rmtree(working_dir, ignore_errors=True)
 
97
 
 
98
 
 
99
class AccountStatsCollector(DatabaseStatsCollector):
 
100
    """
 
101
    Extract storage stats from account databases on the account
 
102
    storage nodes
 
103
    """
 
104
 
 
105
    def __init__(self, stats_conf):
 
106
        super(AccountStatsCollector, self).__init__(stats_conf, 'account',
 
107
                                                    account_server_data_dir,
 
108
                                                    'stats-%Y%m%d%H_')
 
109
 
 
110
    def get_data(self, db_path):
 
111
        """
 
112
        Data for generated csv has the following columns:
 
113
        Account Hash, Container Count, Object Count, Bytes Used
 
114
        """
 
115
        line_data = None
 
116
        broker = AccountBroker(db_path)
 
117
        if not broker.is_deleted():
 
118
            info = broker.get_info()
 
119
            line_data = '"%s",%d,%d,%d\n' % (info['account'],
 
120
                                             info['container_count'],
 
121
                                             info['object_count'],
 
122
                                             info['bytes_used'])
 
123
        return line_data
 
124
 
 
125
 
 
126
class ContainerStatsCollector(DatabaseStatsCollector):
 
127
    """
 
128
    Extract storage stats from container databases on the container
 
129
    storage nodes
 
130
    """
 
131
 
 
132
    def __init__(self, stats_conf):
 
133
        super(ContainerStatsCollector, self).__init__(stats_conf, 'container',
 
134
                                                   container_server_data_dir,
 
135
                                                   'container-stats-%Y%m%d%H_')
 
136
 
 
137
    def get_data(self, db_path):
 
138
        """
 
139
        Data for generated csv has the following columns:
 
140
        Account Hash, Container Name, Object Count, Bytes Used
 
141
        """
 
142
        line_data = None
 
143
        broker = ContainerBroker(db_path)
 
144
        if not broker.is_deleted():
 
145
            info = broker.get_info()
 
146
            encoded_container_name = urllib.quote(info['container'])
 
147
            line_data = '"%s","%s",%d,%d\n' % (
 
148
                info['account'],
 
149
                encoded_container_name,
 
150
                info['object_count'],
 
151
                info['bytes_used'])
 
152
        return line_data