cli.py 10.9 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 inspect
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

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

125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
    def do_help(self, line):
        """Get help
            help <cmd>            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)
142

143
144
145
146
147
148
149
    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
150
151
152
        """
        try:
            {
153
154
155
156
                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
157
158
            }[len(args)](*args)
        except Exception as e:
159
160
            LOG.debug('%s\n' % e)
            sys.stderr.write(self.config_list.__doc__ + '\n')
161

162
163
164
165
166
167
168
    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
        """
169
170
        try:
            {
171
172
                3: self.cnf_cmds.set_global_setting,
                4: self.cnf_cmds.set_setting
173
174
            }[len(args)](*args)
        except Exception as e:
175
176
177
178
179
            LOG.debug('%s\n' % e)
            sys.stderr.write(self.config_set.__doc__ + '\n')

    def config_delete(self, args):
        """Delete an option
180
181
182
        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
183
        """
184
185
186
187
188
        try:
            args.remove('-y')
            args.append(True)
        except ValueError:
            args.append(False)
189
190
        try:
            {
191
192
193
                3: self.cnf_cmds.delete_global_option if (
                    args[0] == 'global') else self.cnf_cmds.delete_section,
                4: self.cnf_cmds.delete_section_option
194
195
196
197
            }[len(args)](*args)
        except Exception as e:
            LOG.debug('%s\n' % e)
            sys.stderr.write(self.config_delete.__doc__ + '\n')
198

199
200
201
202
203
204
205
206
207
208
209
210
211
    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')

212
213
    def do_status(self, line):
        """Get Agkyra client status. Status may be one of the following:
214
215
216
            Syncing     There is a process syncing right now
            Paused      Notifiers are active but syncing is paused
            Not running No active processes
217
218
219
220
221
        """
        client = self.client
        if client:
            # Ask the server for the status
            status = client.get_status()
222
223
            msg = 'Paused' if status['paused'] else 'Syncing'
            sys.stdout.write('%s\n' % msg)
224
225
226
        else:
            sys.stdout.write('Not running\n')
        sys.stdout.flush()
227

228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
    # 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 ...')
245
            protocol.launch_server()
246
            sys.stderr.write(' ... ')
247
            self.helper.wait_session_to_load()
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
            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')
282
283
        sys.stderr.flush()

284
285
    def do_shutdown(self, line):
        """Shutdown Agkyra, if it is running"""
286
287
288
        client = self.client
        if client:
            client.shutdown()
289
            sys.stderr.write('Shutting down Agkyra ... ')
290
291
292
293
            success = self.helper.wait_session_to_stop()
            sys.stderr.write('Stopped' if success else 'Still up (timed out)')
            sys.stderr.write('\n')
        else:
294
            sys.stderr.write('Not running\n')
295
        sys.stderr.flush()