Project

General

Profile

root / trunk / src / haizea / cli / rpc_commands.py @ 675

1
# -------------------------------------------------------------------------- #
2
# Copyright 2006-2009, University of Chicago                                 #
3
# Copyright 2008-2009, Distributed Systems Architecture Group, Universidad   #
4
# Complutense de Madrid (dsa-research.org)                                   #
5
#                                                                            #
6
# Licensed under the Apache License, Version 2.0 (the "License"); you may    #
7
# not use this file except in compliance with the License. You may obtain    #
8
# a copy of the License at                                                   #
9
#                                                                            #
10
# http://www.apache.org/licenses/LICENSE-2.0                                 #
11
#                                                                            #
12
# Unless required by applicable law or agreed to in writing, software        #
13
# distributed under the License is distributed on an "AS IS" BASIS,          #
14
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   #
15
# See the License for the specific language governing permissions and        #
16
# limitations under the License.                                             #
17
# -------------------------------------------------------------------------- #
18
import haizea.common.defaults as defaults
19
import haizea.common.constants as constants
20
from haizea.core.leases import Lease, Capacity, Duration, Timestamp, DiskImageSoftwareEnvironment
21
from haizea.common.utils import round_datetime
22
from haizea.cli.optionparser import Option
23
from haizea.cli import Command
24
import xmlrpclib
25
import sys
26
from mx.DateTime import ISO, now, DateTimeDelta, Parser
27

    
28
try:
29
    import xml.etree.ElementTree as ET
30
except ImportError:
31
    # Compatibility with Python <=2.4
32
    import elementtree.ElementTree as ET 
33

    
34
class RPCCommand(Command):
35
    def __init__(self, argv):
36
        Command.__init__(self, argv)
37
        self.optparser.add_option(Option("-s", "--server", action="store", type="string", dest="server", default=defaults.RPC_URI,
38
                                         help = """
39
                                         Haizea RPC server URI. If not specified, the default %s is used
40
                                         """ % defaults.RPC_URI))
41

    
42
    def create_rpc_proxy(self, server):
43
        return xmlrpclib.ServerProxy(server, allow_none=True)
44

    
45
class haizea_request_lease(RPCCommand):
46
    """
47
    Requests a new lease.
48
    """
49
    
50
    name = "haizea-request-lease"
51

    
52
    START_NOW = "now"
53
    START_BESTEFFORT = "best_effort"
54
    DURATION_UNLIMITED = "unlimited"    
55
    
56
    def __init__(self, argv):
57
        RPCCommand.__init__(self, argv)
58
        
59
        self.optparser.add_option(Option("-f", "--file", action="store", type="string", dest="file",
60
                                         help = """
61
                                         File containing a lease description in XML.
62
                                         """))
63
        self.optparser.add_option(Option("-t", "--start", action="store", type="string", dest="start",
64
                                         help = """
65
                                         Starting time. Can be an ISO timestamp, "best_effort", or "now"
66
                                         """))
67
        self.optparser.add_option(Option("-d", "--duration", action="store", type="string", dest="duration",
68
                                         help = """
69
                                         Duration. Can be an ISO timestamp or "unlimited"
70
                                         """))
71
        self.optparser.add_option(Option("-n", "--numnodes", action="store", type="int", dest="numnodes",
72
                                         help = """
73
                                         Number of nodes.
74
                                         """))
75
        self.optparser.add_option(Option("--preemptible", action="store_true", dest="preemptible",
76
                                         help = """
77
                                         Specifies a preemptible lease.
78
                                         """))
79
        self.optparser.add_option(Option("--non-preemptible", action="store_false", dest="preemptible",
80
                                         help = """
81
                                         Specifies a non-preemptible lease.
82
                                         """))
83
        self.optparser.add_option(Option("-c", "--cpu", action="store", type="int", dest="cpu",
84
                                         help = """
85
                                         Percentage of CPU (must be 0 < c <= 100)
86
                                         """))
87
        self.optparser.add_option(Option("-m", "--mem", action="store", type="int", dest="mem",
88
                                         help = """
89
                                         Memory per node (in MB)
90
                                         """))
91
        self.optparser.add_option(Option("-i", "--vmimage", action="store", type="string", dest="vmimage",
92
                                         help = """
93
                                         Disk image identifier.
94
                                         """))
95
        self.optparser.add_option(Option("-z", "--vmimagesize", action="store", type="int", dest="vmimagesize",
96
                                         help = """
97
                                         Disk image size.
98
                                         """))
99
        
100
    def run(self):
101
        self.parse_options()
102
        
103
        if self.opt.file != None:
104
            lease_elem = ET.parse(self.opt.file).getroot()
105
            # If a relative starting time is used, replace for an
106
            # absolute starting time.
107
            exact = lease.find("start/exact")
108
            if exact != None:
109
                exact_time = exact.get("time")
110
                exact.set("time", str(self.__absolute_time(exact_time)))            
111
            lease_xml_str = ET.tostring(lease_elem)
112
        else:
113
            if self.opt.preemptible == None:
114
                preemptible = False
115
            else:
116
                preemptible = self.opt.preemptible
117
            
118
            capacity = Capacity([constants.RES_CPU, constants.RES_MEM])
119
            capacity.set_quantity(constants.RES_CPU, int(self.opt.cpu) * 100)
120
            capacity.set_quantity(constants.RES_MEM, int(self.opt.mem))    
121
            requested_resources = dict([(i+1, capacity) for i in range(self.opt.numnodes)])    
122
            if self.opt.duration == haizea_request_lease.DURATION_UNLIMITED:
123
                # This is an interim solution (make it run for a century).
124
                # TODO: Integrate concept of unlimited duration in the lease datastruct
125
                duration = DateTimeDelta(36500)
126
            else:
127
                duration = ISO.ParseTimeDelta(self.opt.duration)
128
    
129
            if self.opt.start == haizea_request_lease.START_NOW:
130
                lease = Lease(lease_id = None,
131
                              submit_time = None,
132
                              requested_resources = requested_resources, 
133
                              start = Timestamp(Timestamp.NOW),
134
                              duration = Duration(duration),
135
                              deadline = None, 
136
                              preemptible=preemptible,
137
                              software = DiskImageSoftwareEnvironment(self.opt.vmimage, self.opt.vmimagesize),
138
                              state = None
139
                              )
140
            elif self.opt.start == haizea_request_lease.START_BESTEFFORT:
141
                lease = Lease(lease_id = None,
142
                              submit_time = None,
143
                              requested_resources = requested_resources, 
144
                              start = Timestamp(Timestamp.UNSPECIFIED),
145
                              duration = Duration(duration),
146
                              deadline = None, 
147
                              preemptible=preemptible,
148
                              software = DiskImageSoftwareEnvironment(self.opt.vmimage, self.opt.vmimagesize),
149
                              state = None
150
                              )
151
            else:
152
                start = self.__absolute_time(self.opt.start)
153
                lease = Lease(lease_id = None,
154
                              submit_time = None,
155
                              requested_resources = requested_resources, 
156
                              start = Timestamp(start),
157
                              duration = Duration(duration),
158
                              deadline = None, 
159
                              preemptible=preemptible,
160
                              software = DiskImageSoftwareEnvironment(self.opt.vmimage, self.opt.vmimagesize),
161
                              state = None
162
                              )
163

    
164
            lease_xml_str = ET.tostring(lease.to_xml())
165

    
166
        server = self.create_rpc_proxy(self.opt.server)
167
        
168
        try:
169
            lease_id = server.create_lease(lease_xml_str)
170
            print "Lease submitted correctly."
171
            print "Lease ID: %i" % lease_id
172
        except xmlrpclib.Fault, err:
173
            print >> sys.stderr, "XMLRPC fault: %s" % err.faultString
174
            if self.opt.debug:
175
                raise
176
        except Exception, msg:
177
            print >> sys.stderr, "Error: %s" % msg
178
            if self.opt.debug:
179
                raise
180

    
181
    def __absolute_time(self, time_str):
182
        if time_str[0] == "+":
183
            # Relative time
184
            time = round_datetime(now() + ISO.ParseTime(time_str[1:]))
185
        else:
186
            time = Parser.ParseDateTime(time_str)
187
            
188
        return time
189
        
190
class haizea_cancel_lease(RPCCommand):
191
    """
192
    Cancel a lease.
193
    """
194
    
195
    name = "haizea-cancel-lease"
196
    
197
    def __init__(self, argv):
198
        RPCCommand.__init__(self, argv)
199
        
200
        self.optparser.add_option(Option("-l", "--lease", action="store", type="int", dest="lease",
201
                                         help = """
202
                                         ID of lease to cancel.
203
                                         """))
204
        
205
    def run(self):
206
        self.parse_options()
207
        
208
        server = self.create_rpc_proxy(self.opt.server)
209
        
210
        try:
211
            code = server.cancel_lease(self.opt.lease)
212
        except xmlrpclib.Fault, err:
213
            print >> sys.stderr, "XMLRPC fault: %s" % err.faultString
214
            if self.opt.debug:
215
                raise
216
        except Exception, msg:
217
            print >> sys.stderr, "Error: %s" % msg
218
            if self.opt.debug:
219
                raise
220

    
221
        
222
class haizea_list_leases(RPCCommand):
223
    """
224
    List all active leases in the system (i.e., not including completed or cancelled leases)
225
    """
226
    
227
    name = "haizea-list-leases"
228
    
229
    def __init__(self, argv):
230
        RPCCommand.__init__(self, argv)
231
                
232
    def run(self):
233
        self.parse_options()
234
        
235
        server = self.create_rpc_proxy(self.opt.server)
236
        
237
        fields = [("id","ID", 3),
238
                  ("type","Type", 12),
239
                  ("state","State", 9),
240
                  ("start_req", "Starting time", 22),
241
                  ("duration_req", "Duration", 12),
242
                  ("numnodes", "Nodes", 3)]
243
        
244
        try:
245
            leases = server.get_leases()
246
            leases_fields = []
247
            for lease_xml in leases:
248
                lease = Lease.from_xml_string(lease_xml)
249
                lease_fields = {}
250
                lease_fields["id"] = lease.id
251
                lease_fields["type"] = Lease.type_str[lease.get_type()]
252
                lease_fields["state"] = Lease.state_str[lease.get_state()]
253
                lease_fields["start_req"] = lease.start.requested
254
                lease_fields["duration_req"] = lease.duration.requested
255
                lease_fields["numnodes"] = len(lease.requested_resources)
256
                leases_fields.append(lease_fields)
257
            console_table_printer(fields, leases_fields)
258
        except xmlrpclib.Fault, err:
259
            print >> sys.stderr, "XMLRPC fault: %s" % err.faultString
260
            if self.opt.debug:
261
                raise
262
        except Exception, msg:
263
            print >> sys.stderr, "Error: %s" % msg
264
            if self.opt.debug:
265
                raise
266

    
267

    
268
class haizea_list_hosts(RPCCommand):
269
    """
270
    List hosts managed by Haizea
271
    """
272
    
273
    name = "haizea-list-hosts"
274
    
275
    def __init__(self, argv):
276
        RPCCommand.__init__(self, argv)
277
                
278
    def run(self):
279
        self.parse_options()
280
        
281
        server = self.create_rpc_proxy(self.opt.server)
282
        
283
        fields = [("id","ID", 3),
284
                  ("hostname","Hostname", 10),
285
                  ("cpu","CPUs", 6),
286
                  ("mem", "Mem", 6)]
287
        
288
        try:
289
            hosts = server.get_hosts()
290
            console_table_printer(fields, hosts)
291
        except xmlrpclib.Fault, err:
292
            print >> sys.stderr, "XMLRPC fault: %s" % err.faultString
293
            if self.opt.debug:
294
                raise
295
        except Exception, msg:
296
            print >> sys.stderr, "Error: %s" % msg
297
            if self.opt.debug:
298
                raise
299

    
300
class haizea_show_queue(RPCCommand):
301
    """
302
    Show the contents of the Haizea queue
303
    """
304
    
305
    name = "haizea-show-queue"
306
    
307
    def __init__(self, argv):
308
        RPCCommand.__init__(self, argv)
309
                
310
    def run(self):
311
        self.parse_options()
312
        
313
        server = self.create_rpc_proxy(self.opt.server)
314
        
315
        fields = [("id","ID", 3),
316
                  ("type","Type", 12),
317
                  ("state","State", 9),
318
                  ("start_req", "Starting time", 22),
319
                  ("duration_req", "Duration", 12),
320
                  ("numnodes", "Nodes", 3)]
321
        
322
        try:
323
            leases = server.get_queue()
324
            if len(leases) == 0:
325
                print "Queue is empty."
326
            else:
327
                leases_fields = []
328
                for lease_xml in leases:
329
                    lease = Lease.from_xml_string(lease_xml)
330
                    lease_fields = {}
331
                    lease_fields["id"] = lease.id
332
                    lease_fields["type"] = Lease.type_str[lease.get_type()]
333
                    lease_fields["state"] = Lease.state_str[lease.get_state()]
334
                    lease_fields["start_req"] = lease.start.requested
335
                    lease_fields["duration_req"] = lease.duration.requested
336
                    lease_fields["numnodes"] = len(lease.requested_resources)
337
                    leases_fields.append(lease_fields)
338
                console_table_printer(fields, leases_fields)                
339
        except xmlrpclib.Fault, err:
340
            print >> sys.stderr, "XMLRPC fault: %s" % err.faultString
341
            if self.opt.debug:
342
                raise
343
        except Exception, msg:
344
            print >> sys.stderr, "Error: %s" % msg
345
            if self.opt.debug:
346
                raise
347

    
348

    
349
def console_table_printer(fields, values):
350
    print "\33[1m\33[4m",
351
    for (name,pname,width) in fields:
352
        width = max(len(pname),width) + 1
353
        centered = pname.ljust(width)
354
        print centered,
355
    print "\33[0m"
356
    for v in values:
357
        for (name,pname,width) in fields:
358
            width = max(len(name),width)
359
            print " %s" % str(v[name]).ljust(width),
360
        print