burnin: improve instance create message
[ganeti-local] / tools / burnin
index 4ded749..dc1205a 100755 (executable)
@@ -23,6 +23,7 @@
 
 """
 
+import os
 import sys
 import optparse
 from itertools import izip, islice, cycle
@@ -83,6 +84,8 @@ class Burner(object):
     """Acumulate feedback in our buffer."""
     self._feed_buf.write(msg)
     self._feed_buf.write("\n")
+    if self.opts.verbose:
+      Log(msg)
 
   def ExecOp(self, op):
     """Execute an opcode and manage the exec buffer."""
@@ -121,25 +124,41 @@ class Burner(object):
     parser.add_option("--no-failover", dest="do_failover",
                       help="Skip instance failovers", action="store_false",
                       default=True)
+    parser.add_option("--no-importexport", dest="do_importexport",
+                      help="Skip instance export/import", action="store_false",
+                      default=True)
+    parser.add_option("--no-startstop", dest="do_startstop",
+                      help="Skip instance stop/start", action="store_false",
+                      default=True)
     parser.add_option("-t", "--disk-template", dest="disk_template",
-                      choices=("remote_raid1", "drbd"),
-                      default="remote_raid1",
-                      help="Template type for network mirroring (remote_raid1"
-                      " or drbd) [remote_raid1]")
+                      choices=("diskless", "plain", "remote_raid1", "drbd"),
+                      default="drbd",
+                      help="Template type (plain, diskless, drbd, remote_raid1)"
+                      " [drbd]")
     parser.add_option("-n", "--nodes", dest="nodes", default="",
                       help="Comma separated list of nodes to perform"
                       " the burnin on (defaults to all nodes)")
+    parser.add_option("--iallocator", dest="iallocator",
+                      default=None, type="string",
+                      help="Perform the allocation using an iallocator"
+                      " instead of fixed node spread (node restrictions no"
+                      " longer apply, therefore -n/--nodes must not be used")
 
     options, args = parser.parse_args()
     if len(args) < 1 or options.os is None:
       Usage()
 
-    supported_disk_templates = (constants.DT_PLAIN, constants.DT_REMOTE_RAID1,
+    supported_disk_templates = (constants.DT_DISKLESS, constants.DT_PLAIN,
+                                constants.DT_REMOTE_RAID1,
                                 constants.DT_DRBD8)
     if options.disk_template not in supported_disk_templates:
       Log("Unknown disk template '%s'" % options.disk_template)
       sys.exit(1)
 
+    if options.nodes and options.iallocator:
+      Log("Give either the nodes option or the iallocator option, not both")
+      sys.exit(1)
+
     self.opts = options
     self.instances = args
 
@@ -188,6 +207,16 @@ class Burner(object):
                  islice(cycle(self.nodes), 1, None),
                  self.instances)
     for pnode, snode, instance in mytor:
+      if self.opts.iallocator:
+        pnode = snode = None
+        Log("- Add instance %s (iallocator: %s)" %
+              (instance, self.opts.iallocator))
+      elif self.opts.disk_template not in constants.DTS_NET_MIRROR:
+        snode = None
+        Log("- Add instance %s on node %s" % (instance, pnode))
+      else:
+        Log("- Add instance %s on nodes %s/%s" % (instance, pnode, snode))
+
       op = opcodes.OpCreateInstance(instance_name=instance,
                                     mem_size=128,
                                     disk_size=self.opts.os_size,
@@ -203,8 +232,9 @@ class Burner(object):
                                     wait_for_sync=True,
                                     mac="auto",
                                     kernel_path=None,
-                                    initrd_path=None)
-      Log("- Add instance %s on node %s" % (instance, pnode))
+                                    initrd_path=None,
+                                    hvm_boot_order=None,
+                                    iallocator=self.opts.iallocator)
       self.ExecOp(op)
       self.to_rem.append(instance)
 
@@ -257,6 +287,55 @@ class Burner(object):
       Log("- Failover instance %s" % (instance))
       self.ExecOp(op)
 
+  def ImportExport(self):
+    """Export the instance, delete it, and import it back.
+
+    """
+
+    mytor = izip(cycle(self.nodes),
+                 islice(cycle(self.nodes), 1, None),
+                 islice(cycle(self.nodes), 2, None),
+                 self.instances)
+
+    for pnode, snode, enode, instance in mytor:
+      exp_op = opcodes.OpExportInstance(instance_name=instance,
+                                           target_node=enode,
+                                           shutdown=True)
+      rem_op = opcodes.OpRemoveInstance(instance_name=instance)
+      nam_op = opcodes.OpQueryInstances(output_fields=["name"],
+                                           names=[instance])
+      full_name = self.ExecOp(nam_op)[0][0]
+      imp_dir = os.path.join(constants.EXPORT_DIR, full_name)
+      imp_op = opcodes.OpCreateInstance(instance_name=instance,
+                                        mem_size=128,
+                                        disk_size=self.opts.os_size,
+                                        swap_size=self.opts.swap_size,
+                                        disk_template=self.opts.disk_template,
+                                        mode=constants.INSTANCE_IMPORT,
+                                        src_node=enode,
+                                        src_path=imp_dir,
+                                        pnode=pnode,
+                                        snode=snode,
+                                        vcpus=1,
+                                        start=True,
+                                        ip_check=True,
+                                        wait_for_sync=True,
+                                        mac="auto")
+      erem_op = opcodes.OpRemoveExport(instance_name=instance)
+
+      Log("- Export instance %s to node %s" % (instance, enode))
+      self.ExecOp(exp_op)
+      Log("- Remove instance %s" % (instance))
+      self.ExecOp(rem_op)
+      self.to_rem.remove(instance)
+      Log("- Import instance %s from node %s to node %s" %
+          (instance, enode, pnode))
+      self.ExecOp(imp_op)
+      Log("- Remove export of instance %s" % (instance))
+      self.ExecOp(erem_op)
+
+      self.to_rem.append(instance)
+
   def StopStart(self):
     """Stop/start the instances."""
     for instance in self.instances:
@@ -286,9 +365,10 @@ class Burner(object):
 
     Log("- Testing global parameters")
 
-    if len(self.nodes) == 1 and opts.disk_template != constants.DT_PLAIN:
+    if (len(self.nodes) == 1 and
+        opts.disk_template not in (constants.DT_DISKLESS, constants.DT_PLAIN)):
       Log("When one node is available/selected the disk template must"
-               " be 'plain'")
+               " be 'plain' or 'diskless'")
       sys.exit(1)
 
     has_err = True
@@ -306,7 +386,12 @@ class Burner(object):
       if opts.do_failover and opts.disk_template in constants.DTS_NET_MIRROR:
         self.Failover()
 
-      self.StopStart()
+      if opts.do_importexport:
+        self.ImportExport()
+
+      if opts.do_startstop:
+        self.StopStart()
+
       has_err = False
     finally:
       if has_err: