Project

General

Profile

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

1
# -------------------------------------------------------------------------- #
2
# Copyright 2006-2008, University of Chicago                                 #
3
# Copyright 2008, 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 OptionParser, Option
23
from haizea.cli import Command
24
import xmlrpclib
25
import sys
26
from mx.DateTime import TimeDelta, ISO, now
27
import xml.etree.ElementTree as ET
28

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

    
37
    def create_rpc_proxy(self, server):
38
        return xmlrpclib.ServerProxy(server, allow_none=True)
39

    
40
class haizea_request_lease(RPCCommand):
41
    """
42
    Requests a new lease.
43
    """
44
    
45
    name = "haizea-request-lease"
46

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

    
159
            lease_xml_str = ET.tostring(lease.to_xml())
160

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

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

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

    
262

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

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

    
343

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