cli.py 10.3 KB
Newer Older
Giorgos Korfiatis's avatar
Giorgos Korfiatis committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 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 <http://www.gnu.org/licenses/>.

16
17
18
import cmd
import sys
import logging
19
import time
20
from agkyra import config, protocol, protocol_client
21
22
23
24
25


LOG = logging.getLogger(__name__)


26
27
class ConfigCommands:
    """Commands for handling Agkyra config options"""
28
29
30
31
32
33
    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)
34
        sys.stdout.write('  %s: %s\n' % (option, value))
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59

    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)

60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
    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()


104
class AgkyraCLI(cmd.Cmd):
105
106
    """The CLI for Agkyra is connected to a protocol server"""
    cnf_cmds = ConfigCommands()
107
108
109
110
111
112
113
114
115
116
117
118
    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 = self.helper.load_active_session()
            if session:
                self._client = protocol_client.UIClient(session)
                self._client.connect()
        return self._client
119
120
121
122
123
124
125
126
127
128
129
130

    def preloop(self):
        """Prepare agkyra shell"""
        self.prompt = '\xe2\x9a\x93 '
        self.default('')

    def precmd(self):
        print 'PRE'

    def postcmd(self):
        print 'POST'

131
132
133
134
135
136
137
    def config_list(self, args):
        """List (all or some) options
        list                                List all options
        list <global | cloud | sync>        List global | cloud | sync options
        list global OPTION                  Get global option
        list <cloud | sync> NAME            List options a cloud or sync
        list <cloud | sync> NAME OPTION     List an option from a cloud or sync
138
139
140
        """
        try:
            {
141
142
143
144
                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
145
146
            }[len(args)](*args)
        except Exception as e:
147
148
            LOG.debug('%s\n' % e)
            sys.stderr.write(self.config_list.__doc__ + '\n')
149

150
151
152
153
154
155
156
    def config_set(self, args):
        """Set an option
        set global OPTION VALUE                 Set a global option
        set <cloud | sync> NAME OPTION VALUE    Set an option on cloud or sync
                                                Creates a sync or cloud, if it
                                                does not exist
        """
157
158
        try:
            {
159
160
                3: self.cnf_cmds.set_global_setting,
                4: self.cnf_cmds.set_setting
161
162
            }[len(args)](*args)
        except Exception as e:
163
164
165
166
167
            LOG.debug('%s\n' % e)
            sys.stderr.write(self.config_set.__doc__ + '\n')

    def config_delete(self, args):
        """Delete an option
168
169
170
        delete global OPTION [-y]               Delete a global option
        delete <cloud | sync> NAME [-y]         Delete a sync or cloud
        delete <cloud |sync> NAME OPTION [-y]   Delete a sync or cloud option
171
        """
172
173
174
175
176
        try:
            args.remove('-y')
            args.append(True)
        except ValueError:
            args.append(False)
177
178
        try:
            {
179
180
181
                3: self.cnf_cmds.delete_global_option if (
                    args[0] == 'global') else self.cnf_cmds.delete_section,
                4: self.cnf_cmds.delete_section_option
182
183
184
185
            }[len(args)](*args)
        except Exception as e:
            LOG.debug('%s\n' % e)
            sys.stderr.write(self.config_delete.__doc__ + '\n')
186

187
188
189
190
191
192
193
194
195
196
197
198
199
    def do_config(self, line):
        """Commands for managing the agkyra settings
        list   [global|cloud|sync [setting]]          List all or some settings
        set    <global|cloud|sync> <setting> <value>  Set a setting
        delete <global|cloud|sync> [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')

200
201
    def do_status(self, line):
        """Get Agkyra client status. Status may be one of the following:
202
203
204
            Syncing     There is a process syncing right now
            Paused      Notifiers are active but syncing is paused
            Not running No active processes
205
206
207
208
209
        """
        client = self.client
        if client:
            # Ask the server for the status
            status = client.get_status()
210
211
            msg = 'Paused' if status['paused'] else 'Syncing'
            sys.stdout.write('%s\n' % msg)
212
213
214
        else:
            sys.stdout.write('Not running\n')
        sys.stdout.flush()
215

216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
    # 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 ...')
233
            protocol.launch_server()
234
            sys.stderr.write(' ... ')
235
            self.helper.wait_session_to_load()
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
            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')
270
271
        sys.stderr.flush()

272
273
    def do_shutdown(self, line):
        """Shutdown Agkyra, if it is running"""
274
275
276
        client = self.client
        if client:
            client.shutdown()
277
            sys.stderr.write('Shutting down Agkyra ... ')
278
279
280
281
            success = self.helper.wait_session_to_stop()
            sys.stderr.write('Stopped' if success else 'Still up (timed out)')
            sys.stderr.write('\n')
        else:
282
            sys.stderr.write('Not running\n')
283
        sys.stderr.flush()