Package haizea :: Package cli :: Module commands
[hide private]
[frames] | no frames]

Source Code for Module haizea.cli.commands

  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   
 19  from haizea.core.manager import Manager 
 20  from haizea.common.utils import generate_config_name, unpickle 
 21  from haizea.core.configfile import HaizeaConfig, HaizeaMultiConfig 
 22  from haizea.core.accounting import AccountingDataCollection 
 23  from haizea.common.config import ConfigException 
 24  from haizea.cli.optionparser import Option 
 25  from haizea.cli import Command 
 26  from mx.DateTime import TimeDelta 
 27  import haizea.common.defaults as defaults 
 28  import sys 
 29  import os 
 30  import errno 
 31  import signal 
 32  from time import sleep 
 33   
 34  try: 
 35      import xml.etree.ElementTree as ET 
 36  except ImportError: 
 37      # Compatibility with Python <=2.4 
 38      import elementtree.ElementTree as ET  
 39   
 40   
41 -class haizea(Command):
42 """ 43 This is the main Haizea command. By default, it will start Haizea as a daemon, which 44 can receive requests via RPC or interact with other components such as OpenNebula. It can 45 also start as a foreground process, and write all log messages to the console. All 46 Haizea options are specified through the configuration file.""" 47 48 name = "haizea" 49
50 - def __init__(self, argv):
51 Command.__init__(self, argv) 52 53 self.optparser.add_option(Option("-c", "--conf", action="store", type="string", dest="conf", 54 help = """ 55 The location of the Haizea configuration file. If not 56 specified, Haizea will first look for it in 57 /etc/haizea/haizea.conf and then in ~/.haizea/haizea.conf. 58 """)) 59 self.optparser.add_option(Option("-f", "--fg", action="store_true", dest="foreground", 60 help = """ 61 Runs Haizea in the foreground. 62 """)) 63 self.optparser.add_option(Option("--stop", action="store_true", dest="stop", 64 help = """ 65 Stops the Haizea daemon. 66 """))
67
68 - def run(self):
69 self.parse_options() 70 71 pidfile = defaults.DAEMON_PIDFILE # TODO: Make configurable 72 73 if self.opt.stop == None: 74 # Start Haizea 75 76 # Check if a daemon is already running 77 if os.path.exists(pidfile): 78 pf = file(pidfile,'r') 79 pid = int(pf.read().strip()) 80 pf.close() 81 82 try: 83 os.kill(pid, signal.SIG_DFL) 84 except OSError, (err, msg): 85 if err == errno.ESRCH: 86 # Pidfile is stale. Remove it. 87 os.remove(pidfile) 88 else: 89 msg = "Unexpected error when checking pid file '%s'.\n%s\n" %(pidfile, msg) 90 sys.stderr.write(msg) 91 sys.exit(1) 92 else: 93 msg = "Haizea seems to be already running (pid %i)\n" % pid 94 sys.stderr.write(msg) 95 sys.exit(1) 96 97 try: 98 configfile=self.opt.conf 99 if configfile == None: 100 # Look for config file in default locations 101 for loc in defaults.CONFIG_LOCATIONS: 102 if os.path.exists(loc): 103 config = HaizeaConfig.from_file(loc) 104 break 105 else: 106 print >> sys.stdout, "No configuration file specified, and none found at default locations." 107 print >> sys.stdout, "Make sure a config file exists at:\n -> %s" % "\n -> ".join(defaults.CONFIG_LOCATIONS) 108 print >> sys.stdout, "Or specify a configuration file with the --conf option." 109 exit(1) 110 else: 111 config = HaizeaConfig.from_file(configfile) 112 except ConfigException, msg: 113 print >> sys.stderr, "Error in configuration file:" 114 print >> sys.stderr, msg 115 exit(1) 116 117 daemon = not self.opt.foreground 118 119 manager = Manager(config, daemon, pidfile) 120 121 manager.start() 122 elif self.opt.stop: # Stop Haizea 123 # Based on code in: http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/66012 124 try: 125 pf = file(pidfile,'r') 126 pid = int(pf.read().strip()) 127 pf.close() 128 except IOError: 129 msg = "Could not stop, pid file '%s' missing.\n" 130 sys.stderr.write(msg % pidfile) 131 sys.exit(1) 132 try: 133 while 1: 134 os.kill(pid, signal.SIGTERM) 135 sleep(1) 136 except OSError, err: 137 err = str(err) 138 if err.find("No such process") > 0: 139 os.remove(pidfile) 140 else: 141 print str(err) 142 sys.exit(1)
143
144 -class haizea_generate_configs(Command):
145 """ 146 Takes an Haizea multiconfiguration file and generates the individual 147 configuration files. See the Haizea manual for more details on multiconfiguration 148 files.""" 149 150 name = "haizea-generate-configs" 151
152 - def __init__(self, argv):
153 Command.__init__(self, argv) 154 155 self.optparser.add_option(Option("-c", "--conf", action="store", type="string", dest="conf", required=True, 156 help = """ 157 Multiconfiguration file. 158 """)) 159 self.optparser.add_option(Option("-d", "--dir", action="store", type="string", dest="dir", required=True, 160 help = """ 161 Directory where the individual configuration files 162 must be created. 163 """))
164
165 - def run(self):
166 self.parse_options() 167 168 configfile=self.opt.conf 169 multiconfig = HaizeaMultiConfig.from_file(configfile) 170 171 etcdir = self.opt.dir 172 173 configs = multiconfig.get_configs() 174 175 etcdir = os.path.abspath(etcdir) 176 if not os.path.exists(etcdir): 177 os.makedirs(etcdir) 178 179 for c in configs: 180 profile = c.get_attr("profile") 181 tracefile = c.get("tracefile") 182 injfile = c.get("injectionfile") 183 configname = generate_config_name(profile, tracefile, injfile) 184 configfile = etcdir + "/%s.conf" % configname 185 fc = open(configfile, "w") 186 c.config.write(fc) 187 fc.close()
188
189 -class haizea_generate_scripts(Command):
190 """ 191 Generates a script, based on a script template, to run all the individual 192 configuration files generated by haizea-generate-configs. This command 193 requires Mako Templates for Python (http://www.makotemplates.org/).""" 194 195 name = "haizea-generate-scripts" 196
197 - def __init__(self, argv):
198 Command.__init__(self, argv) 199 200 self.optparser.add_option(Option("-c", "--conf", action="store", type="string", dest="conf", required=True, 201 help = """ 202 Multiconfiguration file used in haizea-generate-configs. 203 """)) 204 self.optparser.add_option(Option("-d", "--confdir", action="store", type="string", dest="confdir", required=True, 205 help = """ 206 Directory containing the individual configuration files. 207 """)) 208 self.optparser.add_option(Option("-t", "--template", action="store", type="string", dest="template", required=True, 209 help = """ 210 Script template (sample templates are included in /usr/share/haizea/etc) 211 """)) 212 self.optparser.add_option(Option("-m", "--only-missing", action="store_true", dest="onlymissing", 213 help = """ 214 If specified, the generated script will only run the configurations 215 that have not already produced a datafile. This is useful when some simulations 216 fail, and you don't want to have to rerun them all. 217 """))
218
219 - def run(self):
220 self.parse_options() 221 222 configfile=self.opt.conf 223 multiconfig = HaizeaMultiConfig.from_file(configfile) 224 225 try: 226 from mako.template import Template 227 except Exception, e: 228 print "You need Mako Templates for Python to run this command." 229 print "You can download them at http://www.makotemplates.org/" 230 exit(1) 231 232 configs = multiconfig.get_configs() 233 234 etcdir = os.path.abspath(self.opt.confdir) 235 if not os.path.exists(etcdir): 236 os.makedirs(etcdir) 237 238 templatedata = [] 239 for c in configs: 240 profile = c.get_attr("profile") 241 tracefile = c.get("tracefile") 242 injfile = c.get("injectionfile") 243 datafile = c.get("datafile") 244 configname = generate_config_name(profile, tracefile, injfile) 245 if not self.opt.onlymissing or not os.path.exists(datafile): 246 configfile = etcdir + "/%s.conf" % configname 247 templatedata.append((configname, configfile)) 248 249 template = Template(filename=self.opt.template) 250 print template.render(configs=templatedata, etcdir=etcdir)
251 252
253 -class haizea_convert_data(Command):
254 """ 255 Converts Haizea datafiles into another (easier to process) format. 256 """ 257 258 name = "haizea-convert-data" 259
260 - def __init__(self, argv):
261 Command.__init__(self, argv) 262 263 self.optparser.add_option(Option("-t", "--type", action="store", dest="type", 264 choices = ["per-run", "per-lease", "counter"], 265 help = """ 266 Type of data to produce. 267 """)) 268 self.optparser.add_option(Option("-c", "--counter", action="store", dest="counter", 269 help = """ 270 Counter to print out when using '--type counter'. 271 """)) 272 self.optparser.add_option(Option("-f", "--format", action="store", type="string", dest="format", 273 help = """ 274 Output format. Currently supported: csv 275 """)) 276 self.optparser.add_option(Option("-l", "--list-counters", action="store_true", dest="list_counters", 277 help = """ 278 If specified, the command will just print out the names of counters 279 stored in the data file and then exit, regardless of other parameters. 280 """))
281
282 - def run(self):
283 self.parse_options() 284 285 datafiles=self.args[1:] 286 if len(datafiles) == 0: 287 print "Please specify at least one datafile to convert" 288 exit(1) 289 290 datafile1 = unpickle(datafiles[0]) 291 292 counter_names = datafile1.counters.keys() 293 attr_names = datafile1.attrs.keys() 294 lease_stats_names = datafile1.lease_stats_names 295 stats_names = datafile1.stats_names 296 297 if self.opt.list_counters: 298 for counter in counter_names: 299 print counter 300 exit(0) 301 302 if self.opt.type == "per-run": 303 header_fields = attr_names + stats_names 304 elif self.opt.type == "per-lease": 305 header_fields = attr_names + ["lease_id"] + lease_stats_names 306 elif self.opt.type == "counter": 307 counter = self.opt.counter 308 if not datafile1.counters.has_key(counter): 309 print "The specified datafile does not have a counter called '%s'" % counter 310 exit(1) 311 header_fields = attr_names + ["time", "value"] 312 if datafile1.counter_avg_type[counter] != AccountingDataCollection.AVERAGE_NONE: 313 header_fields.append("average") 314 315 header = ",".join(header_fields) 316 317 print header 318 319 for datafile in datafiles: 320 data = unpickle(datafile) 321 322 attrs = [data.attrs[attr_name] for attr_name in attr_names] 323 324 if self.opt.type == "per-run": 325 fields = attrs + [`data.stats[stats_name]` for stats_name in stats_names] 326 print ",".join(fields) 327 elif self.opt.type == "per-lease": 328 leases = data.lease_stats 329 for lease_id, lease_stat in leases.items(): 330 fields = attrs + [`lease_id`] + [`lease_stat.get(lease_stat_name,"")` for lease_stat_name in lease_stats_names] 331 print ",".join(fields) 332 elif self.opt.type == "counter": 333 for (time, lease_id, value, avg) in data.counters[counter]: 334 fields = attrs + [`time`, `value`] 335 if data.counter_avg_type[counter] != AccountingDataCollection.AVERAGE_NONE: 336 fields.append(`avg`) 337 print ",".join(fields)
338 339 340
341 -class haizea_lwf2xml(Command):
342 """ 343 Converts old Haizea LWF file into new XML-based LWF format 344 """ 345 346 name = "haizea-lwf2xml" 347
348 - def __init__(self, argv):
349 Command.__init__(self, argv) 350 351 self.optparser.add_option(Option("-i", "--in", action="store", type="string", dest="inf", 352 help = """ 353 Input file 354 """)) 355 self.optparser.add_option(Option("-o", "--out", action="store", type="string", dest="outf", 356 help = """ 357 Output file 358 """))
359
360 - def run(self):
361 self.parse_options() 362 363 infile = self.opt.inf 364 outfile = self.opt.outf 365 366 root = ET.Element("lease-workload") 367 root.set("name", infile) 368 description = ET.SubElement(root, "description") 369 time = TimeDelta(seconds=0) 370 lease_id = 1 371 requests = ET.SubElement(root, "lease-requests") 372 373 374 infile = open(infile, "r") 375 for line in infile: 376 if line[0]!='#' and len(line.strip()) != 0: 377 fields = line.split() 378 submit_time = int(fields[0]) 379 start_time = int(fields[1]) 380 duration = int(fields[2]) 381 real_duration = int(fields[3]) 382 num_nodes = int(fields[4]) 383 cpu = int(fields[5]) 384 mem = int(fields[6]) 385 disk = int(fields[7]) 386 vm_image = fields[8] 387 vm_imagesize = int(fields[9]) 388 389 390 391 lease_request = ET.SubElement(requests, "lease-request") 392 lease_request.set("arrival", str(TimeDelta(seconds=submit_time))) 393 if real_duration != duration: 394 realduration = ET.SubElement(lease_request, "realduration") 395 realduration.set("time", str(TimeDelta(seconds=real_duration))) 396 397 lease = ET.SubElement(lease_request, "lease") 398 lease.set("id", `lease_id`) 399 400 401 nodes = ET.SubElement(lease, "nodes") 402 node_set = ET.SubElement(nodes, "node-set") 403 node_set.set("numnodes", `num_nodes`) 404 res = ET.SubElement(node_set, "res") 405 res.set("type", "CPU") 406 if cpu == 1: 407 res.set("amount", "100") 408 else: 409 pass 410 res = ET.SubElement(node_set, "res") 411 res.set("type", "Memory") 412 res.set("amount", `mem`) 413 414 start = ET.SubElement(lease, "start") 415 if start_time == -1: 416 lease.set("preemptible", "true") 417 else: 418 lease.set("preemptible", "false") 419 exact = ET.SubElement(start, "exact") 420 exact.set("time", str(TimeDelta(seconds=start_time))) 421 422 duration_elem = ET.SubElement(lease, "duration") 423 duration_elem.set("time", str(TimeDelta(seconds=duration))) 424 425 software = ET.SubElement(lease, "software") 426 diskimage = ET.SubElement(software, "disk-image") 427 diskimage.set("id", vm_image) 428 diskimage.set("size", `vm_imagesize`) 429 430 431 lease_id += 1 432 tree = ET.ElementTree(root) 433 print ET.tostring(root)
434 #tree.write("page.xhtml") 435