Unify the “--backend-parameters” option
[ganeti-local] / lib / hypervisor / hv_kvm.py
index 7e2fdea..fd51ae5 100644 (file)
@@ -48,32 +48,45 @@ class KVMHypervisor(hv_base.BaseHypervisor):
   _CONF_DIR = _ROOT_DIR + "/conf" # contains instances startup data
   _DIRS = [_ROOT_DIR, _PIDS_DIR, _CTRL_DIR, _CONF_DIR]
 
   _CONF_DIR = _ROOT_DIR + "/conf" # contains instances startup data
   _DIRS = [_ROOT_DIR, _PIDS_DIR, _CTRL_DIR, _CONF_DIR]
 
-  PARAMETERS = [
-    constants.HV_KERNEL_PATH,
-    constants.HV_INITRD_PATH,
-    constants.HV_ROOT_PATH,
-    constants.HV_KERNEL_ARGS,
-    constants.HV_ACPI,
-    constants.HV_SERIAL_CONSOLE,
-    constants.HV_VNC_BIND_ADDRESS,
-    constants.HV_VNC_TLS,
-    constants.HV_VNC_X509,
-    constants.HV_VNC_X509_VERIFY,
-    constants.HV_CDROM_IMAGE_PATH,
-    constants.HV_BOOT_ORDER,
-    constants.HV_NIC_TYPE,
-    constants.HV_DISK_TYPE,
-    constants.HV_USB_MOUSE,
-    ]
+  PARAMETERS = {
+    constants.HV_KERNEL_PATH: hv_base.OPT_FILE_CHECK,
+    constants.HV_INITRD_PATH: hv_base.OPT_FILE_CHECK,
+    constants.HV_ROOT_PATH: hv_base.NO_CHECK,
+    constants.HV_KERNEL_ARGS: hv_base.NO_CHECK,
+    constants.HV_ACPI: hv_base.NO_CHECK,
+    constants.HV_SERIAL_CONSOLE: hv_base.NO_CHECK,
+    constants.HV_VNC_BIND_ADDRESS:
+      (False, lambda x: (utils.IsValidIP(x) or utils.IsNormAbsPath(x)),
+       "the VNC bind address must be either a valid IP address or an absolute"
+       " pathname", None, None),
+    constants.HV_VNC_TLS: hv_base.NO_CHECK,
+    constants.HV_VNC_X509: hv_base.OPT_DIR_CHECK,
+    constants.HV_VNC_X509_VERIFY: hv_base.NO_CHECK,
+    constants.HV_CDROM_IMAGE_PATH: hv_base.OPT_FILE_CHECK,
+    constants.HV_BOOT_ORDER:
+      hv_base.ParamInSet(True, constants.HT_KVM_VALID_BO_TYPES),
+    constants.HV_NIC_TYPE:
+      hv_base.ParamInSet(True, constants.HT_KVM_VALID_NIC_TYPES),
+    constants.HV_DISK_TYPE:
+      hv_base.ParamInSet(True, constants.HT_KVM_VALID_DISK_TYPES),
+    constants.HV_USB_MOUSE:
+      hv_base.ParamInSet(False, constants.HT_KVM_VALID_MOUSE_TYPES),
+    }
 
   _MIGRATION_STATUS_RE = re.compile('Migration\s+status:\s+(\w+)',
                                     re.M | re.I)
 
 
   _MIGRATION_STATUS_RE = re.compile('Migration\s+status:\s+(\w+)',
                                     re.M | re.I)
 
+  _KVM_NETWORK_SCRIPT = constants.SYSCONFDIR + "/ganeti/kvm-vif-bridge"
+
+  ANCILLARY_FILES = [
+    _KVM_NETWORK_SCRIPT,
+    ]
+
   def __init__(self):
     hv_base.BaseHypervisor.__init__(self)
     # Let's make sure the directories we need exist, even if the RUN_DIR lives
     # in a tmpfs filesystem or has been otherwise wiped out.
   def __init__(self):
     hv_base.BaseHypervisor.__init__(self)
     # Let's make sure the directories we need exist, even if the RUN_DIR lives
     # in a tmpfs filesystem or has been otherwise wiped out.
-    dirs = [(dir, constants.RUN_DIRS_MODE) for dir in self._DIRS]
+    dirs = [(dname, constants.RUN_DIRS_MODE) for dname in self._DIRS]
     utils.EnsureDirs(dirs)
 
   def _InstancePidAlive(self, instance_name):
     utils.EnsureDirs(dirs)
 
   def _InstancePidAlive(self, instance_name):
@@ -107,6 +120,16 @@ class KVMHypervisor(hv_base.BaseHypervisor):
     """
     return '%s/%s.runtime' % (cls._CONF_DIR, instance_name)
 
     """
     return '%s/%s.runtime' % (cls._CONF_DIR, instance_name)
 
+  @classmethod
+  def _RemoveInstanceRuntimeFiles(cls, pidfile, instance_name):
+    """Removes an instance's rutime sockets/files.
+
+    """
+    utils.RemoveFile(pidfile)
+    utils.RemoveFile(cls._InstanceMonitor(instance_name))
+    utils.RemoveFile(cls._InstanceSerial(instance_name))
+    utils.RemoveFile(cls._InstanceKVMRuntime(instance_name))
+
   def _WriteNetScript(self, instance, seq, nic):
     """Write a script to connect a net interface to the proper bridge.
 
   def _WriteNetScript(self, instance, seq, nic):
     """Write a script to connect a net interface to the proper bridge.
 
@@ -127,24 +150,37 @@ class KVMHypervisor(hv_base.BaseHypervisor):
     script.write("# this is autogenerated by Ganeti, please do not edit\n#\n")
     script.write("export INSTANCE=%s\n" % instance.name)
     script.write("export MAC=%s\n" % nic.mac)
     script.write("# this is autogenerated by Ganeti, please do not edit\n#\n")
     script.write("export INSTANCE=%s\n" % instance.name)
     script.write("export MAC=%s\n" % nic.mac)
-    script.write("export IP=%s\n" % nic.ip)
-    script.write("export BRIDGE=%s\n" % nic.bridge)
+    if nic.ip:
+      script.write("export IP=%s\n" % nic.ip)
+    script.write("export MODE=%s\n" % nic.nicparams[constants.NIC_MODE])
+    if nic.nicparams[constants.NIC_LINK]:
+      script.write("export LINK=%s\n" % nic.nicparams[constants.NIC_LINK])
+    if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
+      script.write("export BRIDGE=%s\n" % nic.nicparams[constants.NIC_LINK])
     script.write("export INTERFACE=$1\n")
     # TODO: make this configurable at ./configure time
     script.write("export INTERFACE=$1\n")
     # TODO: make this configurable at ./configure time
-    script.write("if [ -x /etc/ganeti/kvm-vif-bridge ]; then\n")
+    script.write("if [ -x '%s' ]; then\n" % self._KVM_NETWORK_SCRIPT)
     script.write("  # Execute the user-specific vif file\n")
     script.write("  # Execute the user-specific vif file\n")
-    script.write("  /etc/ganeti/kvm-vif-bridge\n")
+    script.write("  %s\n" % self._KVM_NETWORK_SCRIPT)
     script.write("else\n")
     script.write("else\n")
-    script.write("  # Connect the interface to the bridge\n")
     script.write("  /sbin/ifconfig $INTERFACE 0.0.0.0 up\n")
     script.write("  /sbin/ifconfig $INTERFACE 0.0.0.0 up\n")
-    script.write("  /usr/sbin/brctl addif $BRIDGE $INTERFACE\n")
+    if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
+      script.write("  # Connect the interface to the bridge\n")
+      script.write("  /usr/sbin/brctl addif $BRIDGE $INTERFACE\n")
+    elif nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_ROUTED:
+      script.write("  # Route traffic targeted at the IP to the interface\n")
+      script.write("  /sbin/ip route add $IP/32 dev $INTERFACE\n")
+      interface_proxy_arp = "/proc/sys/net/ipv4/conf/$INTERFACE/proxy_arp"
+      script.write("  /bin/echo 1 > %s\n" % interface_proxy_arp)
     script.write("fi\n\n")
     # As much as we'd like to put this in our _ROOT_DIR, that will happen to be
     # mounted noexec sometimes, so we'll have to find another place.
     (tmpfd, tmpfile_name) = tempfile.mkstemp()
     tmpfile = os.fdopen(tmpfd, 'w')
     script.write("fi\n\n")
     # As much as we'd like to put this in our _ROOT_DIR, that will happen to be
     # mounted noexec sometimes, so we'll have to find another place.
     (tmpfd, tmpfile_name) = tempfile.mkstemp()
     tmpfile = os.fdopen(tmpfd, 'w')
-    tmpfile.write(script.getvalue())
-    tmpfile.close()
+    try:
+      tmpfile.write(script.getvalue())
+    finally:
+      tmpfile.close()
     os.chmod(tmpfile_name, 0755)
     return tmpfile_name
 
     os.chmod(tmpfile_name, 0755)
     return tmpfile_name
 
@@ -176,11 +212,7 @@ class KVMHypervisor(hv_base.BaseHypervisor):
 
     cmdline_file = "/proc/%s/cmdline" % pid
     try:
 
     cmdline_file = "/proc/%s/cmdline" % pid
     try:
-      fh = open(cmdline_file, 'r')
-      try:
-        cmdline = fh.read()
-      finally:
-        fh.close()
+      cmdline = utils.ReadFile(cmdline_file)
     except EnvironmentError, err:
       raise errors.HypervisorError("Failed to list instance %s: %s" %
                                    (instance_name, err))
     except EnvironmentError, err:
       raise errors.HypervisorError("Failed to list instance %s: %s" %
                                    (instance_name, err))
@@ -194,9 +226,9 @@ class KVMHypervisor(hv_base.BaseHypervisor):
     while arg_list:
       arg =  arg_list.pop(0)
       if arg == '-m':
     while arg_list:
       arg =  arg_list.pop(0)
       if arg == '-m':
-        memory = arg_list.pop(0)
+        memory = int(arg_list.pop(0))
       elif arg == '-smp':
       elif arg == '-smp':
-        vcpus = arg_list.pop(0)
+        vcpus = int(arg_list.pop(0))
 
     return (instance_name, pid, memory, vcpus, stat, times)
 
 
     return (instance_name, pid, memory, vcpus, stat, times)
 
@@ -236,9 +268,9 @@ class KVMHypervisor(hv_base.BaseHypervisor):
       kvm_cmd.extend(['-no-acpi'])
 
     hvp = instance.hvparams
       kvm_cmd.extend(['-no-acpi'])
 
     hvp = instance.hvparams
-    boot_disk = hvp[constants.HV_BOOT_ORDER] == "disk"
-    boot_cdrom = hvp[constants.HV_BOOT_ORDER] == "cdrom"
-    boot_network = hvp[constants.HV_BOOT_ORDER] == "network"
+    boot_disk = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_DISK
+    boot_cdrom = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_CDROM
+    boot_network = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_NETWORK
 
     if boot_network:
       kvm_cmd.extend(['-boot', 'n'])
 
     if boot_network:
       kvm_cmd.extend(['-boot', 'n'])
@@ -492,10 +524,7 @@ class KVMHypervisor(hv_base.BaseHypervisor):
         self._RetryInstancePowerdown(instance, pid)
 
     if not utils.IsProcessAlive(pid):
         self._RetryInstancePowerdown(instance, pid)
 
     if not utils.IsProcessAlive(pid):
-      utils.RemoveFile(pidfile)
-      utils.RemoveFile(self._InstanceMonitor(instance.name))
-      utils.RemoveFile(self._InstanceSerial(instance.name))
-      utils.RemoveFile(self._InstanceKVMRuntime(instance.name))
+      self._RemoveInstanceRuntimeFiles(pidfile, instance.name)
       return True
     else:
       return False
       return True
     else:
       return False
@@ -610,10 +639,7 @@ class KVMHypervisor(hv_base.BaseHypervisor):
           time.sleep(2)
 
     utils.KillProcess(pid)
           time.sleep(2)
 
     utils.KillProcess(pid)
-    utils.RemoveFile(pidfile)
-    utils.RemoveFile(self._InstanceMonitor(instance_name))
-    utils.RemoveFile(self._InstanceSerial(instance_name))
-    utils.RemoveFile(self._InstanceKVMRuntime(instance_name))
+    self._RemoveInstanceRuntimeFiles(pidfile, instance_name)
 
   def GetNodeInfo(self):
     """Return information about the node.
 
   def GetNodeInfo(self):
     """Return information about the node.
@@ -684,105 +710,30 @@ class KVMHypervisor(hv_base.BaseHypervisor):
 
     kernel_path = hvparams[constants.HV_KERNEL_PATH]
     if kernel_path:
 
     kernel_path = hvparams[constants.HV_KERNEL_PATH]
     if kernel_path:
-      if not os.path.isabs(hvparams[constants.HV_KERNEL_PATH]):
-        raise errors.HypervisorError("The kernel path must be an absolute path"
-                                     ", if defined")
-
       if not hvparams[constants.HV_ROOT_PATH]:
       if not hvparams[constants.HV_ROOT_PATH]:
-        raise errors.HypervisorError("Need a root partition for the instance"
-                                     ", if a kernel is defined")
-
-    if hvparams[constants.HV_INITRD_PATH]:
-      if not os.path.isabs(hvparams[constants.HV_INITRD_PATH]):
-        raise errors.HypervisorError("The initrd path must an absolute path"
-                                     ", if defined")
+        raise errors.HypervisorError("Need a root partition for the instance,"
+                                     " if a kernel is defined")
 
 
-    vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
-    if vnc_bind_address:
-      if not utils.IsValidIP(vnc_bind_address):
-        if not os.path.isabs(vnc_bind_address):
-          raise errors.HypervisorError("The VNC bind address must be either"
-                                       " a valid IP address or an absolute"
-                                       " pathname. '%s' given" %
-                                       vnc_bind_address)
-
-    if hvparams[constants.HV_VNC_X509_VERIFY] and \
-      not hvparams[constants.HV_VNC_X509]:
-        raise errors.HypervisorError("%s must be defined, if %s is" %
-                                     (constants.HV_VNC_X509,
-                                      constants.HV_VNC_X509_VERIFY))
-
-    if hvparams[constants.HV_VNC_X509]:
-      if not os.path.isabs(hvparams[constants.HV_VNC_X509]):
-        raise errors.HypervisorError("The vnc x509 path must an absolute path"
-                                     ", if defined")
-
-    iso_path = hvparams[constants.HV_CDROM_IMAGE_PATH]
-    if iso_path and not os.path.isabs(iso_path):
-      raise errors.HypervisorError("The path to the CDROM image must be"
-                                   " an absolute path, if defined")
+    if (hvparams[constants.HV_VNC_X509_VERIFY] and
+        not hvparams[constants.HV_VNC_X509]):
+      raise errors.HypervisorError("%s must be defined, if %s is" %
+                                   (constants.HV_VNC_X509,
+                                    constants.HV_VNC_X509_VERIFY))
 
     boot_order = hvparams[constants.HV_BOOT_ORDER]
 
     boot_order = hvparams[constants.HV_BOOT_ORDER]
-    if boot_order not in ('cdrom', 'disk', 'network'):
-      raise errors.HypervisorError("The boot order must be 'cdrom', 'disk' or"
-                                   " 'network'")
-
-    if boot_order == 'cdrom' and not iso_path:
-      raise errors.HypervisorError("Cannot boot from cdrom without an ISO path")
-
-    nic_type = hvparams[constants.HV_NIC_TYPE]
-    if nic_type not in constants.HT_KVM_VALID_NIC_TYPES:
-      raise errors.HypervisorError("Invalid NIC type %s specified for the KVM"
-                                   " hypervisor. Please choose one of: %s" %
-                                   (nic_type,
-                                    constants.HT_KVM_VALID_NIC_TYPES))
-    elif boot_order == 'network' and nic_type == constants.HT_NIC_PARAVIRTUAL:
+
+    if (boot_order == constants.HT_BO_CDROM and
+        not hvparams[constants.HV_CDROM_IMAGE_PATH]):
+      raise errors.HypervisorError("Cannot boot from cdrom without an"
+                                   " ISO path")
+    if (boot_order == constants.HT_BO_NETWORK and
+        hvparams[constants.HV_NIC_TYPE] == constants.HT_NIC_PARAVIRTUAL):
       raise errors.HypervisorError("Cannot boot from a paravirtual NIC. Please"
       raise errors.HypervisorError("Cannot boot from a paravirtual NIC. Please"
-                                   " change the nic type.")
-
-    disk_type = hvparams[constants.HV_DISK_TYPE]
-    if disk_type not in constants.HT_KVM_VALID_DISK_TYPES:
-      raise errors.HypervisorError("Invalid disk type %s specified for the KVM"
-                                   " hypervisor. Please choose one of: %s" %
-                                   (disk_type,
-                                    constants.HT_KVM_VALID_DISK_TYPES))
-
-    mouse_type = hvparams[constants.HV_USB_MOUSE]
-    if mouse_type and mouse_type not in ('mouse', 'tablet'):
-      raise errors.HypervisorError("Invalid usb mouse type %s specified for"
-                                   " the KVM hyervisor. Please choose"
-                                   " 'mouse' or 'tablet'" % mouse_type)
-
-  def ValidateParameters(self, hvparams):
-    """Check the given parameters for validity.
+                                   " change the NIC type.")
 
 
-    For the KVM hypervisor, this checks the existence of the
-    kernel.
+  @classmethod
+  def PowercycleNode(cls):
+    """KVM powercycle, just a wrapper over Linux powercycle.
 
     """
 
     """
-    super(KVMHypervisor, self).ValidateParameters(hvparams)
-
-    kernel_path = hvparams[constants.HV_KERNEL_PATH]
-    if kernel_path and not os.path.isfile(kernel_path):
-      raise errors.HypervisorError("Instance kernel '%s' not found or"
-                                   " not a file" % kernel_path)
-    initrd_path = hvparams[constants.HV_INITRD_PATH]
-    if initrd_path and not os.path.isfile(initrd_path):
-      raise errors.HypervisorError("Instance initrd '%s' not found or"
-                                   " not a file" % initrd_path)
-
-    vnc_bind_address = hvparams[constants.HV_VNC_BIND_ADDRESS]
-    if vnc_bind_address and not utils.IsValidIP(vnc_bind_address) and \
-       not os.path.isdir(vnc_bind_address):
-       raise errors.HypervisorError("Instance vnc bind address must be either"
-                                    " an ip address or an existing directory")
-
-    vnc_x509 = hvparams[constants.HV_VNC_X509]
-    if vnc_x509 and not os.path.isdir(vnc_x509):
-      raise errors.HypervisorError("Instance vnc x509 path '%s' not found"
-                                   " or not a directory" % vnc_x509)
-
-    iso_path = hvparams[constants.HV_CDROM_IMAGE_PATH]
-    if iso_path and not os.path.isfile(iso_path):
-      raise errors.HypervisorError("Instance cdrom image '%s' not found or"
-                                   " not a file" % iso_path)
+    cls.LinuxPowercycle()