mirror of
https://gitlab.isc.org/isc-projects/kea
synced 2025-09-03 15:35:17 +00:00
[trac930] modify Stats
- remove unneeded subject and listener classes - add StatsError for handling errors in Stats - add some new methods (update_modules, update_statistics_data and get_statistics_data) - modify implementations of existent commands(show and set) according changes stats.spec - remove reset and remove command because stats module couldn't manage other modules' statistics data schema - add implementation of strict validation of each statistics data (If the validation is failed, it puts out the error.) - stats module shows its PID when status command invoked - add new command showschema invokable via bindctl - set command requires arguments of owner module name and statistics item name - show and showschema commands accepts arguments of owner module name and statistics item name - exits at exit code 1 if got runtime errors - has boot time in _BASETIME
This commit is contained in:
@@ -15,16 +15,17 @@
|
||||
# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
|
||||
# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
"""
|
||||
Statistics daemon in BIND 10
|
||||
|
||||
"""
|
||||
import sys; sys.path.append ('@@PYTHONPATH@@')
|
||||
import os
|
||||
import signal
|
||||
import select
|
||||
from time import time, strftime, gmtime
|
||||
from optparse import OptionParser, OptionValueError
|
||||
from collections import defaultdict
|
||||
from isc.config.ccsession import ModuleCCSession, create_answer
|
||||
from isc.cc import Session, SessionError
|
||||
|
||||
import isc
|
||||
import isc.util.process
|
||||
import isc.log
|
||||
from stats_messages import *
|
||||
|
||||
@@ -35,352 +36,24 @@ logger = isc.log.Logger("stats")
|
||||
# have #1074
|
||||
DBG_STATS_MESSAGING = 30
|
||||
|
||||
# This is for boot_time of Stats
|
||||
_BASETIME = gmtime()
|
||||
|
||||
# for setproctitle
|
||||
import isc.util.process
|
||||
isc.util.process.rename()
|
||||
|
||||
# If B10_FROM_SOURCE is set in the environment, we use data files
|
||||
# from a directory relative to that, otherwise we use the ones
|
||||
# installed on the system
|
||||
if "B10_FROM_SOURCE" in os.environ:
|
||||
BASE_LOCATION = os.environ["B10_FROM_SOURCE"] + os.sep + \
|
||||
"src" + os.sep + "bin" + os.sep + "stats"
|
||||
SPECFILE_LOCATION = os.environ["B10_FROM_SOURCE"] + os.sep + \
|
||||
"src" + os.sep + "bin" + os.sep + "stats" + os.sep + "stats.spec"
|
||||
else:
|
||||
PREFIX = "@prefix@"
|
||||
DATAROOTDIR = "@datarootdir@"
|
||||
BASE_LOCATION = "@datadir@" + os.sep + "@PACKAGE@"
|
||||
BASE_LOCATION = BASE_LOCATION.replace("${datarootdir}", DATAROOTDIR).replace("${prefix}", PREFIX)
|
||||
SPECFILE_LOCATION = BASE_LOCATION + os.sep + "stats.spec"
|
||||
SCHEMA_SPECFILE_LOCATION = BASE_LOCATION + os.sep + "stats-schema.spec"
|
||||
|
||||
class Singleton(type):
|
||||
"""
|
||||
A abstract class of singleton pattern
|
||||
"""
|
||||
# Because of singleton pattern:
|
||||
# At the beginning of coding, one UNIX domain socket is needed
|
||||
# for config manager, another socket is needed for stats module,
|
||||
# then stats module might need two sockets. So I adopted the
|
||||
# singleton pattern because I avoid creating multiple sockets in
|
||||
# one stats module. But in the initial version stats module
|
||||
# reports only via bindctl, so just one socket is needed. To use
|
||||
# the singleton pattern is not important now. :(
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
type.__init__(self, *args, **kwargs)
|
||||
self._instances = {}
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
if args not in self._instances:
|
||||
self._instances[args]={}
|
||||
kw = tuple(kwargs.items())
|
||||
if kw not in self._instances[args]:
|
||||
self._instances[args][kw] = type.__call__(self, *args, **kwargs)
|
||||
return self._instances[args][kw]
|
||||
|
||||
class Callback():
|
||||
"""
|
||||
A Callback handler class
|
||||
"""
|
||||
def __init__(self, name=None, callback=None, args=(), kwargs={}):
|
||||
self.name = name
|
||||
self.callback = callback
|
||||
self.args = args
|
||||
self.kwargs = kwargs
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
if not args:
|
||||
args = self.args
|
||||
if not kwargs:
|
||||
kwargs = self.kwargs
|
||||
if self.callback:
|
||||
return self.callback(*args, **kwargs)
|
||||
|
||||
class Subject():
|
||||
"""
|
||||
A abstract subject class of observer pattern
|
||||
"""
|
||||
# Because of observer pattern:
|
||||
# In the initial release, I'm also sure that observer pattern
|
||||
# isn't definitely needed because the interface between gathering
|
||||
# and reporting statistics data is single. However in the future
|
||||
# release, the interfaces may be multiple, that is, multiple
|
||||
# listeners may be needed. For example, one interface, which
|
||||
# stats module has, is for between ''config manager'' and stats
|
||||
# module, another interface is for between ''HTTP server'' and
|
||||
# stats module, and one more interface is for between ''SNMP
|
||||
# server'' and stats module. So by considering that stats module
|
||||
# needs multiple interfaces in the future release, I adopted the
|
||||
# observer pattern in stats module. But I don't have concrete
|
||||
# ideas in case of multiple listener currently.
|
||||
|
||||
def __init__(self):
|
||||
self._listeners = []
|
||||
|
||||
def attach(self, listener):
|
||||
if not listener in self._listeners:
|
||||
self._listeners.append(listener)
|
||||
|
||||
def detach(self, listener):
|
||||
try:
|
||||
self._listeners.remove(listener)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
def notify(self, event, modifier=None):
|
||||
for listener in self._listeners:
|
||||
if modifier != listener:
|
||||
listener.update(event)
|
||||
|
||||
class Listener():
|
||||
"""
|
||||
A abstract listener class of observer pattern
|
||||
"""
|
||||
def __init__(self, subject):
|
||||
self.subject = subject
|
||||
self.subject.attach(self)
|
||||
self.events = {}
|
||||
|
||||
def update(self, name):
|
||||
if name in self.events:
|
||||
callback = self.events[name]
|
||||
return callback()
|
||||
|
||||
def add_event(self, event):
|
||||
self.events[event.name]=event
|
||||
|
||||
class SessionSubject(Subject, metaclass=Singleton):
|
||||
"""
|
||||
A concrete subject class which creates CC session object
|
||||
"""
|
||||
def __init__(self, session=None):
|
||||
Subject.__init__(self)
|
||||
self.session=session
|
||||
self.running = False
|
||||
|
||||
def start(self):
|
||||
self.running = True
|
||||
self.notify('start')
|
||||
|
||||
def stop(self):
|
||||
self.running = False
|
||||
self.notify('stop')
|
||||
|
||||
def check(self):
|
||||
self.notify('check')
|
||||
|
||||
class CCSessionListener(Listener):
|
||||
"""
|
||||
A concrete listener class which creates SessionSubject object and
|
||||
ModuleCCSession object
|
||||
"""
|
||||
def __init__(self, subject):
|
||||
Listener.__init__(self, subject)
|
||||
self.session = subject.session
|
||||
self.boot_time = get_datetime()
|
||||
|
||||
# create ModuleCCSession object
|
||||
self.cc_session = ModuleCCSession(SPECFILE_LOCATION,
|
||||
self.config_handler,
|
||||
self.command_handler,
|
||||
self.session)
|
||||
|
||||
self.session = self.subject.session = self.cc_session._session
|
||||
|
||||
# initialize internal data
|
||||
self.stats_spec = isc.config.module_spec_from_file(SCHEMA_SPECFILE_LOCATION).get_config_spec()
|
||||
self.stats_data = self.initialize_data(self.stats_spec)
|
||||
|
||||
# add event handler invoked via SessionSubject object
|
||||
self.add_event(Callback('start', self.start))
|
||||
self.add_event(Callback('stop', self.stop))
|
||||
self.add_event(Callback('check', self.check))
|
||||
# don't add 'command_' suffix to the special commands in
|
||||
# order to prevent executing internal command via bindctl
|
||||
|
||||
# get commands spec
|
||||
self.commands_spec = self.cc_session.get_module_spec().get_commands_spec()
|
||||
|
||||
# add event handler related command_handler of ModuleCCSession
|
||||
# invoked via bindctl
|
||||
for cmd in self.commands_spec:
|
||||
try:
|
||||
# add prefix "command_"
|
||||
name = "command_" + cmd["command_name"]
|
||||
callback = getattr(self, name)
|
||||
kwargs = self.initialize_data(cmd["command_args"])
|
||||
self.add_event(Callback(name=name, callback=callback, args=(), kwargs=kwargs))
|
||||
except AttributeError as ae:
|
||||
logger.error(STATS_UNKNOWN_COMMAND_IN_SPEC, cmd["command_name"])
|
||||
|
||||
def start(self):
|
||||
"""
|
||||
start the cc chanel
|
||||
"""
|
||||
# set initial value
|
||||
self.stats_data['stats.boot_time'] = self.boot_time
|
||||
self.stats_data['stats.start_time'] = get_datetime()
|
||||
self.stats_data['stats.last_update_time'] = get_datetime()
|
||||
self.stats_data['stats.lname'] = self.session.lname
|
||||
self.cc_session.start()
|
||||
# request Bob to send statistics data
|
||||
logger.debug(DBG_STATS_MESSAGING, STATS_SEND_REQUEST_BOSS)
|
||||
cmd = isc.config.ccsession.create_command("sendstats", None)
|
||||
seq = self.session.group_sendmsg(cmd, 'Boss')
|
||||
self.session.group_recvmsg(True, seq)
|
||||
|
||||
def stop(self):
|
||||
"""
|
||||
stop the cc chanel
|
||||
"""
|
||||
return self.cc_session.close()
|
||||
|
||||
def check(self):
|
||||
"""
|
||||
check the cc chanel
|
||||
"""
|
||||
return self.cc_session.check_command(False)
|
||||
|
||||
def config_handler(self, new_config):
|
||||
"""
|
||||
handle a configure from the cc channel
|
||||
"""
|
||||
logger.debug(DBG_STATS_MESSAGING, STATS_RECEIVED_NEW_CONFIG,
|
||||
new_config)
|
||||
|
||||
# do nothing currently
|
||||
return create_answer(0)
|
||||
|
||||
def command_handler(self, command, *args, **kwargs):
|
||||
"""
|
||||
handle commands from the cc channel
|
||||
"""
|
||||
# add 'command_' suffix in order to executing command via bindctl
|
||||
name = 'command_' + command
|
||||
|
||||
if name in self.events:
|
||||
event = self.events[name]
|
||||
return event(*args, **kwargs)
|
||||
else:
|
||||
return self.command_unknown(command, args)
|
||||
|
||||
def command_shutdown(self, args):
|
||||
"""
|
||||
handle shutdown command
|
||||
"""
|
||||
logger.info(STATS_RECEIVED_SHUTDOWN_COMMAND)
|
||||
self.subject.running = False
|
||||
return create_answer(0)
|
||||
|
||||
def command_set(self, args, stats_data={}):
|
||||
"""
|
||||
handle set command
|
||||
"""
|
||||
# 'args' must be dictionary type
|
||||
self.stats_data.update(args['stats_data'])
|
||||
|
||||
# overwrite "stats.LastUpdateTime"
|
||||
self.stats_data['stats.last_update_time'] = get_datetime()
|
||||
|
||||
return create_answer(0)
|
||||
|
||||
def command_remove(self, args, stats_item_name=''):
|
||||
"""
|
||||
handle remove command
|
||||
"""
|
||||
|
||||
# 'args' must be dictionary type
|
||||
if args and args['stats_item_name'] in self.stats_data:
|
||||
stats_item_name = args['stats_item_name']
|
||||
|
||||
logger.debug(DBG_STATS_MESSAGING, STATS_RECEIVED_REMOVE_COMMAND,
|
||||
stats_item_name)
|
||||
|
||||
# just remove one item
|
||||
self.stats_data.pop(stats_item_name)
|
||||
|
||||
return create_answer(0)
|
||||
|
||||
def command_show(self, args, stats_item_name=''):
|
||||
"""
|
||||
handle show command
|
||||
"""
|
||||
|
||||
# always overwrite 'report_time' and 'stats.timestamp'
|
||||
# if "show" command invoked
|
||||
self.stats_data['report_time'] = get_datetime()
|
||||
self.stats_data['stats.timestamp'] = get_timestamp()
|
||||
|
||||
# if with args
|
||||
if args and args['stats_item_name'] in self.stats_data:
|
||||
stats_item_name = args['stats_item_name']
|
||||
logger.debug(DBG_STATS_MESSAGING,
|
||||
STATS_RECEIVED_SHOW_NAME_COMMAND,
|
||||
stats_item_name)
|
||||
return create_answer(0, {stats_item_name: self.stats_data[stats_item_name]})
|
||||
|
||||
logger.debug(DBG_STATS_MESSAGING,
|
||||
STATS_RECEIVED_SHOW_ALL_COMMAND)
|
||||
return create_answer(0, self.stats_data)
|
||||
|
||||
def command_reset(self, args):
|
||||
"""
|
||||
handle reset command
|
||||
"""
|
||||
logger.debug(DBG_STATS_MESSAGING,
|
||||
STATS_RECEIVED_RESET_COMMAND)
|
||||
|
||||
# re-initialize internal variables
|
||||
self.stats_data = self.initialize_data(self.stats_spec)
|
||||
|
||||
# reset initial value
|
||||
self.stats_data['stats.boot_time'] = self.boot_time
|
||||
self.stats_data['stats.start_time'] = get_datetime()
|
||||
self.stats_data['stats.last_update_time'] = get_datetime()
|
||||
self.stats_data['stats.lname'] = self.session.lname
|
||||
|
||||
return create_answer(0)
|
||||
|
||||
def command_status(self, args):
|
||||
"""
|
||||
handle status command
|
||||
"""
|
||||
logger.debug(DBG_STATS_MESSAGING, STATS_RECEIVED_STATUS_COMMAND)
|
||||
# just return "I'm alive."
|
||||
return create_answer(0, "I'm alive.")
|
||||
|
||||
def command_unknown(self, command, args):
|
||||
"""
|
||||
handle an unknown command
|
||||
"""
|
||||
logger.error(STATS_RECEIVED_UNKNOWN_COMMAND, command)
|
||||
return create_answer(1, "Unknown command: '"+str(command)+"'")
|
||||
|
||||
|
||||
def initialize_data(self, spec):
|
||||
"""
|
||||
initialize stats data
|
||||
"""
|
||||
def __get_init_val(spec):
|
||||
if spec['item_type'] == 'null':
|
||||
return None
|
||||
elif spec['item_type'] == 'boolean':
|
||||
return bool(spec.get('item_default', False))
|
||||
elif spec['item_type'] == 'string':
|
||||
return str(spec.get('item_default', ''))
|
||||
elif spec['item_type'] in set(['number', 'integer']):
|
||||
return int(spec.get('item_default', 0))
|
||||
elif spec['item_type'] in set(['float', 'double', 'real']):
|
||||
return float(spec.get('item_default', 0.0))
|
||||
elif spec['item_type'] in set(['list', 'array']):
|
||||
return spec.get('item_default',
|
||||
[ __get_init_val(s) for s in spec['list_item_spec'] ])
|
||||
elif spec['item_type'] in set(['map', 'object']):
|
||||
return spec.get('item_default',
|
||||
dict([ (s['item_name'], __get_init_val(s)) for s in spec['map_item_spec'] ]) )
|
||||
else:
|
||||
return spec.get('item_default')
|
||||
return dict([ (s['item_name'], __get_init_val(s)) for s in spec ])
|
||||
SPECFILE_LOCATION = "@datadir@" + os.sep + "@PACKAGE@" + os.sep + "stats.spec"
|
||||
SPECFILE_LOCATION = SPECFILE_LOCATION.replace("${datarootdir}", DATAROOTDIR)\
|
||||
.replace("${prefix}", PREFIX)
|
||||
|
||||
def get_timestamp():
|
||||
"""
|
||||
@@ -388,33 +61,314 @@ def get_timestamp():
|
||||
"""
|
||||
return time()
|
||||
|
||||
def get_datetime():
|
||||
def get_datetime(gmt=None):
|
||||
"""
|
||||
get current datetime
|
||||
"""
|
||||
return strftime("%Y-%m-%dT%H:%M:%SZ", gmtime())
|
||||
if not gmt: gmt = gmtime()
|
||||
return strftime("%Y-%m-%dT%H:%M:%SZ", gmt)
|
||||
|
||||
def main(session=None):
|
||||
def parse_spec(spec):
|
||||
"""
|
||||
parse spec type data
|
||||
"""
|
||||
def _parse_spec(spec):
|
||||
item_type = spec['item_type']
|
||||
if item_type == "integer":
|
||||
return int(spec.get('item_default', 0))
|
||||
elif item_type == "real":
|
||||
return float(spec.get('item_default', 0.0))
|
||||
elif item_type == "boolean":
|
||||
return bool(spec.get('item_default', False))
|
||||
elif item_type == "string":
|
||||
return str(spec.get('item_default', ""))
|
||||
elif item_type == "list":
|
||||
return spec.get(
|
||||
"item_default",
|
||||
[ _parse_spec(s) for s in spec["list_item_spec"] ])
|
||||
elif item_type == "map":
|
||||
return spec.get(
|
||||
"item_default",
|
||||
dict([ (s["item_name"], _parse_spec(s)) for s in spec["map_item_spec"] ]) )
|
||||
else:
|
||||
return spec.get("item_default", None)
|
||||
return dict([ (s['item_name'], _parse_spec(s)) for s in spec ])
|
||||
|
||||
class Callback():
|
||||
"""
|
||||
A Callback handler class
|
||||
"""
|
||||
def __init__(self, command=None, args=(), kwargs={}):
|
||||
self.command = command
|
||||
self.args = args
|
||||
self.kwargs = kwargs
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
if not args: args = self.args
|
||||
if not kwargs: kwargs = self.kwargs
|
||||
if self.command: return self.command(*args, **kwargs)
|
||||
|
||||
class StatsError(Exception):
|
||||
"""Exception class for Stats class"""
|
||||
pass
|
||||
|
||||
class Stats:
|
||||
"""
|
||||
Main class of stats module
|
||||
"""
|
||||
def __init__(self):
|
||||
self.running = False
|
||||
# create ModuleCCSession object
|
||||
self.mccs = isc.config.ModuleCCSession(SPECFILE_LOCATION,
|
||||
self.config_handler,
|
||||
self.command_handler)
|
||||
self.cc_session = self.mccs._session
|
||||
# get module spec
|
||||
self.module_name = self.mccs.get_module_spec().get_module_name()
|
||||
self.modules = {}
|
||||
self.statistics_data = {}
|
||||
# get commands spec
|
||||
self.commands_spec = self.mccs.get_module_spec().get_commands_spec()
|
||||
# add event handler related command_handler of ModuleCCSession
|
||||
self.callbacks = {}
|
||||
for cmd in self.commands_spec:
|
||||
# add prefix "command_"
|
||||
name = "command_" + cmd["command_name"]
|
||||
try:
|
||||
callback = getattr(self, name)
|
||||
kwargs = parse_spec(cmd["command_args"])
|
||||
self.callbacks[name] = Callback(command=callback, kwargs=kwargs)
|
||||
except AttributeError:
|
||||
raise StatsError(STATS_UNKNOWN_COMMAND_IN_SPEC, cmd["command_name"])
|
||||
self.mccs.start()
|
||||
|
||||
def start(self):
|
||||
"""
|
||||
Start stats module
|
||||
"""
|
||||
self.running = True
|
||||
# TODO: should be added into new logging interface
|
||||
# if self.verbose:
|
||||
# sys.stdout.write("[b10-stats] starting\n")
|
||||
|
||||
# request Bob to send statistics data
|
||||
logger.debug(DBG_STATS_MESSAGING, STATS_SEND_REQUEST_BOSS)
|
||||
cmd = isc.config.ccsession.create_command("sendstats", None)
|
||||
seq = self.cc_session.group_sendmsg(cmd, 'Boss')
|
||||
self.cc_session.group_recvmsg(True, seq)
|
||||
|
||||
# initialized Statistics data
|
||||
errors = self.update_statistics_data(
|
||||
self.module_name,
|
||||
lname=self.cc_session.lname,
|
||||
boot_time=get_datetime(_BASETIME)
|
||||
)
|
||||
if errors:
|
||||
raise StatsError("stats spec file is incorrect")
|
||||
|
||||
while self.running:
|
||||
self.mccs.check_command(False)
|
||||
|
||||
def config_handler(self, new_config):
|
||||
"""
|
||||
handle a configure from the cc channel
|
||||
"""
|
||||
logger.debug(DBG_STATS_MESSAGING, STATS_RECEIVED_NEW_CONFIG,
|
||||
new_config)
|
||||
# do nothing currently
|
||||
return isc.config.create_answer(0)
|
||||
|
||||
def command_handler(self, command, kwargs):
|
||||
"""
|
||||
handle commands from the cc channel
|
||||
"""
|
||||
name = 'command_' + command
|
||||
if name in self.callbacks:
|
||||
callback = self.callbacks[name]
|
||||
if kwargs:
|
||||
return callback(**kwargs)
|
||||
else:
|
||||
return callback()
|
||||
else:
|
||||
logger.error(STATS_RECEIVED_UNKNOWN_COMMAND, command)
|
||||
return isc.config.create_answer(1, "Unknown command: '"+str(command)+"'")
|
||||
|
||||
def update_modules(self):
|
||||
"""
|
||||
update information of each module
|
||||
"""
|
||||
modules = {}
|
||||
seq = self.cc_session.group_sendmsg(
|
||||
isc.config.ccsession.create_command(
|
||||
isc.config.ccsession.COMMAND_GET_STATISTICS_SPEC),
|
||||
'ConfigManager')
|
||||
(answer, env) = self.cc_session.group_recvmsg(False, seq)
|
||||
if answer:
|
||||
(rcode, value) = isc.config.ccsession.parse_answer(answer)
|
||||
if rcode == 0:
|
||||
for mod in value:
|
||||
spec = { "module_name" : mod,
|
||||
"statistics" : [] }
|
||||
if value[mod] and type(value[mod]) is list:
|
||||
spec["statistics"] = value[mod]
|
||||
modules[mod] = isc.config.module_spec.ModuleSpec(spec)
|
||||
modules[self.module_name] = self.mccs.get_module_spec()
|
||||
self.modules = modules
|
||||
|
||||
def get_statistics_data(self, owner=None, name=None):
|
||||
"""
|
||||
return statistics data which stats module has of each module
|
||||
"""
|
||||
self.update_statistics_data()
|
||||
if owner and name:
|
||||
try:
|
||||
return self.statistics_data[owner][name]
|
||||
except KeyError:
|
||||
pass
|
||||
elif owner:
|
||||
try:
|
||||
return self.statistics_data[owner]
|
||||
except KeyError:
|
||||
pass
|
||||
elif name:
|
||||
pass
|
||||
else:
|
||||
return self.statistics_data
|
||||
|
||||
def update_statistics_data(self, owner=None, **data):
|
||||
"""
|
||||
change statistics date of specified module into specified data
|
||||
"""
|
||||
self.update_modules()
|
||||
statistics_data = {}
|
||||
for (name, module) in self.modules.items():
|
||||
value = parse_spec(module.get_statistics_spec())
|
||||
if module.validate_statistics(True, value):
|
||||
statistics_data[name] = value
|
||||
for (name, value) in self.statistics_data.items():
|
||||
if name in statistics_data:
|
||||
statistics_data[name].update(value)
|
||||
else:
|
||||
statistics_data[name] = value
|
||||
self.statistics_data = statistics_data
|
||||
if owner and data:
|
||||
errors = []
|
||||
try:
|
||||
if self.modules[owner].validate_statistics(False, data, errors):
|
||||
self.statistics_data[owner].update(data)
|
||||
return
|
||||
except KeyError:
|
||||
errors.append('unknown module name')
|
||||
return errors
|
||||
|
||||
def command_status(self):
|
||||
"""
|
||||
handle status command
|
||||
"""
|
||||
logger.debug(DBG_STATS_MESSAGING, STATS_RECEIVED_STATUS_COMMAND)
|
||||
return isc.config.create_answer(
|
||||
0, "Stats is up. (PID " + str(os.getpid()) + ")")
|
||||
|
||||
def command_shutdown(self):
|
||||
"""
|
||||
handle shutdown command
|
||||
"""
|
||||
logger.info(STATS_RECEIVED_SHUTDOWN_COMMAND)
|
||||
self.running = False
|
||||
return isc.config.create_answer(0)
|
||||
|
||||
def command_show(self, owner=None, name=None):
|
||||
"""
|
||||
handle show command
|
||||
"""
|
||||
if (owner or name):
|
||||
logger.debug(DBG_STATS_MESSAGING,
|
||||
STATS_RECEIVED_SHOW_NAME_COMMAND,
|
||||
str(owner)+", "+str(name))
|
||||
else:
|
||||
logger.debug(DBG_STATS_MESSAGING,
|
||||
STATS_RECEIVED_SHOW_ALL_COMMAND)
|
||||
if owner and not name:
|
||||
return isc.config.create_answer(1, "item name is not specified")
|
||||
errors = self.update_statistics_data(
|
||||
self.module_name,
|
||||
timestamp=get_timestamp(),
|
||||
report_time=get_datetime()
|
||||
)
|
||||
if errors: raise StatsError("stats spec file is incorrect")
|
||||
ret = self.get_statistics_data(owner, name)
|
||||
if ret:
|
||||
return isc.config.create_answer(0, ret)
|
||||
else:
|
||||
return isc.config.create_answer(
|
||||
1, "specified module name and/or item name are incorrect")
|
||||
|
||||
def command_showschema(self, owner=None, name=None):
|
||||
"""
|
||||
handle show command
|
||||
"""
|
||||
# TODO: should be added into new logging interface
|
||||
# if self.verbose:
|
||||
# sys.stdout.write("[b10-stats] 'showschema' command received\n")
|
||||
self.update_modules()
|
||||
schema = {}
|
||||
schema_byname = {}
|
||||
for mod in self.modules:
|
||||
spec = self.modules[mod].get_statistics_spec()
|
||||
schema_byname[mod] = {}
|
||||
if spec:
|
||||
schema[mod] = spec
|
||||
for item in spec:
|
||||
schema_byname[mod][item['item_name']] = item
|
||||
if owner:
|
||||
try:
|
||||
if name:
|
||||
return isc.config.create_answer(0, schema_byname[owner][name])
|
||||
else:
|
||||
return isc.config.create_answer(0, schema[owner])
|
||||
except KeyError:
|
||||
pass
|
||||
else:
|
||||
if name:
|
||||
return isc.config.create_answer(1, "module name is not specified")
|
||||
else:
|
||||
return isc.config.create_answer(0, schema)
|
||||
return isc.config.create_answer(
|
||||
1, "specified module name and/or item name are incorrect")
|
||||
|
||||
def command_set(self, owner, data):
|
||||
"""
|
||||
handle set command
|
||||
"""
|
||||
errors = self.update_statistics_data(owner, **data)
|
||||
if errors:
|
||||
return isc.config.create_answer(
|
||||
1,
|
||||
"specified module name and/or statistics data are incorrect: "
|
||||
+ ", ".join(errors))
|
||||
errors = self.update_statistics_data(
|
||||
self.module_name, last_update_time=get_datetime() )
|
||||
if errors:
|
||||
raise StatsError("stats spec file is incorrect")
|
||||
return isc.config.create_answer(0)
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
parser = OptionParser()
|
||||
parser.add_option("-v", "--verbose", dest="verbose", action="store_true",
|
||||
help="display more about what is going on")
|
||||
parser.add_option(
|
||||
"-v", "--verbose", dest="verbose", action="store_true",
|
||||
help="display more about what is going on")
|
||||
(options, args) = parser.parse_args()
|
||||
if options.verbose:
|
||||
isc.log.init("b10-stats", "DEBUG", 99)
|
||||
subject = SessionSubject(session=session)
|
||||
listener = CCSessionListener(subject)
|
||||
subject.start()
|
||||
while subject.running:
|
||||
subject.check()
|
||||
subject.stop()
|
||||
|
||||
stats = Stats()
|
||||
stats.start()
|
||||
except OptionValueError as ove:
|
||||
logger.fatal(STATS_BAD_OPTION_VALUE, ove)
|
||||
except SessionError as se:
|
||||
logger.fatal(STATS_CC_SESSION_ERROR, se)
|
||||
# TODO: should be added into new logging interface
|
||||
except StatsError as se:
|
||||
sys.exit("[b10-stats] %s" % se)
|
||||
except KeyboardInterrupt as kie:
|
||||
logger.info(STATS_STOPPED_BY_KEYBOARD)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
Reference in New Issue
Block a user