# Copyright (C) 2015 GRNET S.A.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
import cmd
import sys
import logging
from agkyra import config, protocol, protocol_client
LOG = logging.getLogger(__name__)
class ConfigCommands:
"""Commands for handling Agkyra config options"""
cnf = config.AgkyraConfig()
def print_option(self, section, name, option):
"""Print a configuration option"""
section = '%s.%s' % (section, name) if name else section
value = self.cnf.get(section, option)
sys.stdout.write(' %s: %s\n' % (option, value))
def list_section(self, section, name):
"""list contents of a section"""
content = dict(self.cnf.items(section))
if section in 'global' and name:
self.print_option(section, '', name)
else:
if name:
content = content[name]
for option in content.keys():
self.print_option(section, name, option)
def list_section_type(self, section):
"""print the contents of a configuration section"""
names = ['', ] if section in ('global', ) else self.cnf.keys(section)
assert names, 'Section %s not found' % section
for name in names:
print section, name
self.list_section(section, name)
def list_sections(self):
"""List all configuration sections"""
for section in self.cnf.sections():
self.list_section_type(section)
def set_global_setting(self, section, option, value):
assert section in ('global'), 'Syntax error'
self.cnf.set(section, option, value)
self.cnf.write()
def set_setting(self, section, name, option, value):
assert section in self.cnf.sections(), 'Syntax error'
self.cnf.set('%s.%s' % (section, name), option, value)
self.cnf.write()
def delete_global_option(self, section, option, yes=False):
"""Delete global option"""
if (not yes and 'y' != raw_input(
'Delete %s option %s? [y|N]: ' % (section, option))):
sys.stderr.write('Aborted\n')
else:
self.cnf.remove_option(section, option)
self.cnf.write()
def delete_section_option(self, section, name, option, yes=False):
"""Delete a section (sync or cloud) option"""
assert section in self.cnf.sections(), 'Syntax error'
if (not yes and 'y' != raw_input(
'Delete %s of %s "%s"? [y|N]: ' % (option, section, name))):
sys.stderr.write('Aborted\n')
else:
if section == config.CLOUD_PREFIX:
self.cnf.remove_from_cloud(name, option)
elif section == config.SYNC_PREFIX:
self.cnf.remove_from_sync(name, option)
else:
self.cnf.remove_option('%s.%s' % (section, name), option)
self.cnf.write()
def delete_section(self, section, name, yes=False):
"""Delete a section (sync or cloud)"""
if (not yes and 'y' != raw_input(
'Delete %s "%s"? [y|N]: ' % (section, name))):
sys.stderr.write('Aborted\n')
else:
self.cnf.remove_option(section, name)
self.cnf.write()
class AgkyraCLI(cmd.Cmd):
"""The CLI for Agkyra is connected to a protocol server"""
cnf_cmds = ConfigCommands()
helper = protocol.SessionHelper()
@property
def client(self):
"""Return the helper client instace or None"""
self._client = getattr(self, '_client', None)
if not self._client:
session = protocol.retry_on_locked_db(
self.helper.load_active_session)
if session:
self._client = protocol_client.UIClient(session)
self._client.connect()
return self._client
def preloop(self):
"""Prepare agkyra shell"""
self.prompt = '\xe2\x9a\x93 '
self.default('')
def do_help(self, line):
"""Get help
help for an individual command
help <--list | -l> for all commands
"""
if line and line in ('-l', '--list'):
prefix = 'do_'
for c in self.get_names():
if c.startswith(prefix):
actual_name = c[len(prefix):]
print '-', actual_name, '-'
self.do_help(actual_name)
print
else:
if not line:
cmd.Cmd.do_help(self, 'help')
cmd.Cmd.do_help(self, line)
def config_list(self, args):
"""List (all or some) options
list List all options
list List global | cloud | sync options
list global OPTION Get global option
list NAME List options a cloud or sync
list NAME OPTION List an option from a cloud or sync
"""
try:
{
0: self.cnf_cmds.list_sections,
1: self.cnf_cmds.list_section_type,
2: self.cnf_cmds.list_section,
3: self.cnf_cmds.print_option
}[len(args)](*args)
except Exception as e:
LOG.debug('%s\n' % e)
sys.stderr.write(self.config_list.__doc__ + '\n')
def config_set(self, args):
"""Set an option
set global OPTION VALUE Set a global option
set NAME OPTION VALUE Set an option on cloud or sync
Creates a sync or cloud, if it
does not exist
"""
try:
{
3: self.cnf_cmds.set_global_setting,
4: self.cnf_cmds.set_setting
}[len(args)](*args)
except Exception as e:
LOG.debug('%s\n' % e)
sys.stderr.write(self.config_set.__doc__ + '\n')
def config_delete(self, args):
"""Delete an option
delete global OPTION [-y] Delete a global option
delete NAME [-y] Delete a sync or cloud
delete NAME OPTION [-y] Delete a sync or cloud option
"""
try:
args.remove('-y')
args.append(True)
except ValueError:
args.append(False)
try:
{
3: self.cnf_cmds.delete_global_option if (
args[0] == 'global') else self.cnf_cmds.delete_section,
4: self.cnf_cmds.delete_section_option
}[len(args)](*args)
except Exception as e:
LOG.debug('%s\n' % e)
sys.stderr.write(self.config_delete.__doc__ + '\n')
def do_config(self, line):
"""Commands for managing the agkyra settings
list [global|cloud|sync [setting]] List all or some settings
set Set a setting
delete [setting] Delete a setting or group
"""
args = line.split(' ')
try:
method = getattr(self, 'config_' + args[0])
method(args[1:])
except AttributeError:
self.do_help('config')
def do_status(self, line):
"""Get Agkyra client status. Status may be one of the following:
Syncing There is a process syncing right now
Paused Notifiers are active but syncing is paused
Not running No active processes
"""
client = self.client
if client:
# Ask the server for the status
status = client.get_status()
msg = 'Paused' if status['paused'] else 'Syncing'
sys.stdout.write('%s\n' % msg)
else:
sys.stdout.write('Not running\n')
sys.stdout.flush()
# def do_start_daemon(self, line):
# """Start the Agkyra daemon if it is not running"""
# if self.client:
# sys.stderr.write('An Agkyra daemon is already running\n')
# else:
# sys.stderr.write('Launcing a new Agkyra daemon\n')
# protocol.launch_server()
# sys.stderr.write('Waiting for the deamon to load\n')
# self.helper.wait_session_to_load()
# self.do_status('')
# sys.stderr.flush()
def do_start(self, line):
"""Start the session. If no daemons are running, start one first"""
client = self.client
if not client:
sys.stderr.write('No Agkyra daemons running, starting one ...')
protocol.launch_server()
sys.stderr.write(' ... ')
self.helper.wait_session_to_load()
sys.stderr.write('OK\n')
else:
status = client.get_status()
if status['paused']:
client.start()
sys.stderr.write('Starting syncer ... ')
try:
client.wait_until_syncing()
except AssertionError:
sys.stderr.write('\n')
raise
sys.stderr.write('OK\n')
else:
sys.stderr.write('Already syncing\n')
sys.stderr.flush()
def do_pause(self, line):
"""Pause a session (stop it from syncing, but keep it running)"""
client = self.client
if client:
status = client.get_status()
if status['paused']:
sys.stderr.write('Already paused\n')
else:
client.pause()
sys.stderr.write('Pausing syncer ... ')
try:
client.wait_until_paused()
except AssertionError:
sys.stderr.write('\n')
raise
sys.stderr.write('OK\n')
else:
sys.stderr.write('Not running (use "start" to launch)\n')
sys.stderr.flush()
def do_shutdown(self, line):
"""Shutdown Agkyra, if it is running"""
client = self.client
if client:
client.shutdown()
sys.stderr.write('Shutting down Agkyra ... ')
success = self.helper.wait_session_to_stop()
sys.stderr.write('Stopped' if success else 'Still up (timed out)')
sys.stderr.write('\n')
else:
sys.stderr.write('Not running\n')
sys.stderr.flush()