summaryrefslogtreecommitdiffstats
path: root/func/overlord/cmd_modules/call.py
blob: bb9a61e67d7510e12e6bbd47916f010ef2558cd3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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
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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
"""
call func method invoker

Copyright 2007, Red Hat, Inc
see AUTHORS

This software may be freely redistributed under the terms of the GNU
general public license.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
"""


import optparse
import pprint
import xmlrpclib
import time

from func.overlord import client
from func.overlord import base_command

import func.jobthing as jobthing

DEFAULT_FORKS = 1

class Call(base_command.BaseCommand):
    name = "call"
    usage = "call module method name arg1 arg2..."
    def addOptions(self):
        self.parser.add_option("-v", "--verbose", dest="verbose",
                               action="store_true")
        self.parser.add_option("-x", "--xmlrpc", dest="xmlrpc",
                               help="output return data in XMLRPC format",
                               action="store_true")
        self.parser.add_option("", "--raw", dest="rawprint",
                               help="output return data using Python print",
                               action="store_true")
        self.parser.add_option("-j", "--json", dest="json",
                               help="output return data using JSON",
                               action="store_true")
        self.parser.add_option("-p", "--pickle", dest="pickle",
                               help="output return data in python pickle format",
                               action="store_true")
        self.parser.add_option("-f", "--forks", dest="forks",
                               help="how many parallel processes?  (default 1)",
                               default=DEFAULT_FORKS)
        self.parser.add_option("-a", "--async", dest="async",
                               help="Use async calls?  (default 0)",
                               action="store_true")
        self.parser.add_option("-n", "--nopoll", dest="nopoll",
                               help="Don't wait for async results",
                               action="store_true")
        self.parser.add_option("", "--sort", dest="sort",
                               help="In async mode, wait for all results and print them sorted.",
                               action="store_true")
        self.parser.add_option("-s", "--jobstatus", dest="jobstatus",
                               help="Do not run any job, just check for status.",
                               action="store_true")

    def handleOptions(self, options):
        self.options = options
        self.verbose = options.verbose

        # I'm not really a fan of the "module methodname" approach
        # but we'll keep it for now -akl

    def parse(self, argv):
        self.argv = argv

        return base_command.BaseCommand.parse(self, argv)
        

    def format_return(self, data):
        """
        The call module supports multiple output return types, the default is pprint.
        """
        
        if self.options.xmlrpc:
            return xmlrpclib.dumps((data,""))

        if self.options.json:
            try:
                import simplejson
                return simplejson.dumps(data)
            except ImportError:
                print "WARNING: json support not found, install python-simplejson"
                return data

        if self.options.rawprint:
            return data

        if self.options.pickle:
            import pickle
            return pickle.dumps(data)

        return  pprint.pformat(data)

    def do(self, args):

        # I'm not really a fan of the "module methodname" approach
        # but we'll keep it for now -akl

        # I kind of feel like we shouldn't be parsing args here, but I'm
        # not sure what the write place is -al;
        self.module           = args[0]
        if len(args) > 1:
            self.method       = args[1]
        else:
            self.method       = None
        if len(args) > 2:
            self.method_args  = args[2:]
        else:
            self.method_args  = []

        # this could get weird, sub sub classes might be calling this
        # this with multiple.parentCommand.parentCommands...
        # maybe command.py needs a way to set attrs on subCommands?
        # or some sort of shared datastruct?
        #        self.getOverlord()


        self.interactive = False
        
        self.server_spec = self.parentCommand.server_spec
        self.getOverlord()
        

        if not self.options.jobstatus:
            results = self.overlord_obj.run(self.module, self.method, self.method_args)
        else:
            (return_code, async_results) = self.overlord_obj.job_status(float(self.module))
            res = self.format_return((return_code, async_results))
            print res
            return async_results

        if self.options.async:
            partial = {}
            if self.options.nopoll:
                print "JOB_ID:", pprint.pformat(results)
                return results
            else:
                async_done = False
                while not async_done:
                    (return_code, async_results) = self.overlord_obj.job_status(results)
                    if return_code == jobthing.JOB_ID_RUNNING:
                        time.sleep(0.1)
                    elif return_code == jobthing.JOB_ID_FINISHED:
                        async_done = True
                        partial = self.print_partial_results(partial, async_results, self.options.sort)
                        return partial
                    elif return_code == jobthing.JOB_ID_PARTIAL:
                        if not self.options.sort:
                            partial = self.print_partial_results(partial, async_results)
                    else:
                        sys.stderr.write("Async error")
                        return 0

        # TO DO: add multiplexer support
        # probably as a higher level module.

        # dump the return code stuff atm till we figure out the right place for it
        foo =  self.format_return(results)
        print foo

        # nothing really makes use of this atm -akl
        return results

    def print_partial_results(self, old, new, sort=0):
        diff = dict([(k, v) for k, v in new.iteritems() if k not in old])
        if len(diff) > 0:
            iter=diff.iteritems()
            if sort:
                iter=sorted(iter)
            for res in iter:
                print self.format_return(res)
            return new
        return old