Project

General

Profile

root / branches / 1.1 / src / haizea / cli / rpc_commands.py @ 847

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 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="float", dest="cpu",
84
                                         help = """
85
                                         Percentage of CPU (must be 0 < c <= 1.0)
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_elem.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 = Parser.TimeDeltaFromString(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
                              user_id = None,
133
                              requested_resources = requested_resources, 
134
                              start = Timestamp(Timestamp.NOW),
135
                              duration = Duration(duration),
136
                              deadline = None, 
137
                              preemptible=preemptible,
138
                              software = DiskImageSoftwareEnvironment(self.opt.vmimage, self.opt.vmimagesize),
139
                              state = None
140
                              )
141
            elif self.opt.start == haizea_request_lease.START_BESTEFFORT:
142
                lease = Lease(lease_id = None,
143
                              submit_time = None,
144
                              user_id = None,                              
145
                              requested_resources = requested_resources, 
146
                              start = Timestamp(Timestamp.UNSPECIFIED),
147
                              duration = Duration(duration),
148
                              deadline = None, 
149
                              preemptible=preemptible,
150
                              software = DiskImageSoftwareEnvironment(self.opt.vmimage, self.opt.vmimagesize),
151
                              state = None
152
                              )
153
            else:
154
                start = self.__absolute_time(self.opt.start)
155
                lease = Lease(lease_id = None,
156
                              submit_time = None,
157
                              user_id = None,                              
158
                              requested_resources = requested_resources, 
159
                              start = Timestamp(start),
160
                              duration = Duration(duration),
161
                              deadline = None, 
162
                              preemptible=preemptible,
163
                              software = DiskImageSoftwareEnvironment(self.opt.vmimage, self.opt.vmimagesize),
164
                              state = None
165
                              )
166

    
167
            lease_xml_str = ET.tostring(lease.to_xml())
168

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

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

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

    
270

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

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

    
351

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