Revision 376631d1

b/Makefile.am
1297 1297
	  echo "SSH_CONSOLE_USER = '$(SSH_CONSOLE_USER)'"; \
1298 1298
	  echo "EXPORT_DIR = '$(EXPORT_DIR)'"; \
1299 1299
	  echo "OS_SEARCH_PATH = [$(OS_SEARCH_PATH)]"; \
1300
	  echo "ES_SEARCH_PATH = [$(ES_SEARCH_PATH)]"; \
1300 1301
	  echo "XEN_BOOTLOADER = '$(XEN_BOOTLOADER)'"; \
1301 1302
	  echo "XEN_KERNEL = '$(XEN_KERNEL)'"; \
1302 1303
	  echo "XEN_INITRD = '$(XEN_INITRD)'"; \
b/configure.ac
60 60
  [os_search_path="'/srv/ganeti/os'"])
61 61
AC_SUBST(OS_SEARCH_PATH, $os_search_path)
62 62

  
63
# --with-extstorage-search-path=...
64
# same black sed magic for quoting of the strings in the list
65
AC_ARG_WITH([extstorage-search-path],
66
  [AS_HELP_STRING([--with-extstorage-search-path=LIST],
67
    [comma separated list of directories to]
68
    [ search for External Storage Providers]
69
    [ (default is /srv/ganeti/extstorage)]
70
  )],
71
  [es_search_path=`echo -n "$withval" | sed -e "s/\([[^,]]*\)/'\1'/g"`],
72
  [es_search_path="'/srv/ganeti/extstorage'"])
73
AC_SUBST(ES_SEARCH_PATH, $es_search_path)
74

  
63 75
# --with-iallocator-search-path=...
64 76
# do a bit of black sed magic to for quoting of the strings in the list
65 77
AC_ARG_WITH([iallocator-search-path],
b/lib/bdev.py
2786 2786
                  result.fail_reason, result.output)
2787 2787

  
2788 2788

  
2789
class ExtStorageDevice(BlockDev):
2790
  """A block device provided by an ExtStorage Provider.
2791

  
2792
  This class implements the External Storage Interface, which means
2793
  handling of the externally provided block devices.
2794

  
2795
  """
2796
  def __init__(self, unique_id, children, size, params):
2797
    """Attaches to an extstorage block device.
2798

  
2799
    """
2800
    super(ExtStorageDevice, self).__init__(unique_id, children, size, params)
2801
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
2802
      raise ValueError("Invalid configuration data %s" % str(unique_id))
2803

  
2804
    self.driver, self.vol_name = unique_id
2805

  
2806
    self.major = self.minor = None
2807
    self.Attach()
2808

  
2809
  @classmethod
2810
  def Create(cls, unique_id, children, size, params):
2811
    """Create a new extstorage device.
2812

  
2813
    Provision a new volume using an extstorage provider, which will
2814
    then be mapped to a block device.
2815

  
2816
    """
2817
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
2818
      raise errors.ProgrammerError("Invalid configuration data %s" %
2819
                                   str(unique_id))
2820

  
2821
    # Call the External Storage's create script,
2822
    # to provision a new Volume inside the External Storage
2823
    _ExtStorageAction(constants.ES_ACTION_CREATE, unique_id, str(size))
2824

  
2825
    return ExtStorageDevice(unique_id, children, size, params)
2826

  
2827
  def Remove(self):
2828
    """Remove the extstorage device.
2829

  
2830
    """
2831
    if not self.minor and not self.Attach():
2832
      # The extstorage device doesn't exist.
2833
      return
2834

  
2835
    # First shutdown the device (remove mappings).
2836
    self.Shutdown()
2837

  
2838
    # Call the External Storage's remove script,
2839
    # to remove the Volume from the External Storage
2840
    _ExtStorageAction(constants.ES_ACTION_REMOVE, self.unique_id)
2841

  
2842
  def Rename(self, new_id):
2843
    """Rename this device.
2844

  
2845
    """
2846
    pass
2847

  
2848
  def Attach(self):
2849
    """Attach to an existing extstorage device.
2850

  
2851
    This method maps the extstorage volume that matches our name with
2852
    a corresponding block device and then attaches to this device.
2853

  
2854
    """
2855
    self.attached = False
2856

  
2857
    # Call the External Storage's attach script,
2858
    # to attach an existing Volume to a block device under /dev
2859
    self.dev_path = _ExtStorageAction(constants.ES_ACTION_ATTACH,
2860
                                      self.unique_id)
2861

  
2862
    try:
2863
      st = os.stat(self.dev_path)
2864
    except OSError, err:
2865
      logging.error("Error stat()'ing %s: %s", self.dev_path, str(err))
2866
      return False
2867

  
2868
    if not stat.S_ISBLK(st.st_mode):
2869
      logging.error("%s is not a block device", self.dev_path)
2870
      return False
2871

  
2872
    self.major = os.major(st.st_rdev)
2873
    self.minor = os.minor(st.st_rdev)
2874
    self.attached = True
2875

  
2876
    return True
2877

  
2878
  def Assemble(self):
2879
    """Assemble the device.
2880

  
2881
    """
2882
    pass
2883

  
2884
  def Shutdown(self):
2885
    """Shutdown the device.
2886

  
2887
    """
2888
    if not self.minor and not self.Attach():
2889
      # The extstorage device doesn't exist.
2890
      return
2891

  
2892
    # Call the External Storage's detach script,
2893
    # to detach an existing Volume from it's block device under /dev
2894
    _ExtStorageAction(constants.ES_ACTION_DETACH, self.unique_id)
2895

  
2896
    self.minor = None
2897
    self.dev_path = None
2898

  
2899
  def Open(self, force=False):
2900
    """Make the device ready for I/O.
2901

  
2902
    """
2903
    pass
2904

  
2905
  def Close(self):
2906
    """Notifies that the device will no longer be used for I/O.
2907

  
2908
    """
2909
    pass
2910

  
2911
  def Grow(self, amount, dryrun, backingstore):
2912
    """Grow the Volume.
2913

  
2914
    @type amount: integer
2915
    @param amount: the amount (in mebibytes) to grow with
2916
    @type dryrun: boolean
2917
    @param dryrun: whether to execute the operation in simulation mode
2918
        only, without actually increasing the size
2919

  
2920
    """
2921
    if not backingstore:
2922
      return
2923
    if not self.Attach():
2924
      _ThrowError("Can't attach to extstorage device during Grow()")
2925

  
2926
    if dryrun:
2927
      # we do not support dry runs of resize operations for now.
2928
      return
2929

  
2930
    new_size = self.size + amount
2931

  
2932
    # Call the External Storage's grow script,
2933
    # to grow an existing Volume inside the External Storage
2934
    _ExtStorageAction(constants.ES_ACTION_GROW, self.unique_id,
2935
                      str(self.size), grow=str(new_size))
2936

  
2937
  def SetInfo(self, text):
2938
    """Update metadata with info text.
2939

  
2940
    """
2941
    # Replace invalid characters
2942
    text = re.sub("^[^A-Za-z0-9_+.]", "_", text)
2943
    text = re.sub("[^-A-Za-z0-9_+.]", "_", text)
2944

  
2945
    # Only up to 128 characters are allowed
2946
    text = text[:128]
2947

  
2948
    # Call the External Storage's setinfo script,
2949
    # to set metadata for an existing Volume inside the External Storage
2950
    _ExtStorageAction(constants.ES_ACTION_SETINFO, self.unique_id,
2951
                      metadata=text)
2952

  
2953

  
2954
def _ExtStorageAction(action, unique_id, size=None, grow=None, metadata=None):
2955
  """Take an External Storage action.
2956

  
2957
  Take an External Storage action concerning or affecting
2958
  a specific Volume inside the External Storage.
2959

  
2960
  @type action: string
2961
  @param action: which action to perform. One of:
2962
                 create / remove / grow / attach / detach
2963
  @type unique_id: tuple (driver, vol_name)
2964
  @param unique_id: a tuple containing the type of ExtStorage (driver)
2965
                    and the Volume name
2966
  @type size: integer
2967
  @param size: the size of the Volume in mebibytes
2968
  @type grow: integer
2969
  @param grow: the new size in mebibytes (after grow)
2970
  @type metadata: string
2971
  @param metadata: metadata info of the Volume, for use by the provider
2972
  @rtype: None or a block device path (during attach)
2973

  
2974
  """
2975
  driver, vol_name = unique_id
2976

  
2977
  # Create an External Storage instance of type `driver'
2978
  status, inst_es = ExtStorageFromDisk(driver)
2979
  if not status:
2980
    _ThrowError("%s" % inst_es)
2981

  
2982
  # Create the basic environment for the driver's scripts
2983
  create_env = _ExtStorageEnvironment(unique_id, size, grow, metadata)
2984

  
2985
  # Do not use log file for action `attach' as we need
2986
  # to get the output from RunResult
2987
  # TODO: find a way to have a log file for attach too
2988
  logfile = None
2989
  if action is not constants.ES_ACTION_ATTACH:
2990
    logfile = _VolumeLogName(action, driver, vol_name)
2991

  
2992
  # Make sure the given action results in a valid script
2993
  if action not in constants.ES_SCRIPTS:
2994
    _ThrowError("Action '%s' doesn't result in a valid ExtStorage script" %
2995
                action)
2996

  
2997
  # Find out which external script to run according the given action
2998
  script_name = action + "_script"
2999
  script = getattr(inst_es, script_name)
3000

  
3001
  # Run the external script
3002
  result = utils.RunCmd([script], env=create_env,
3003
                        cwd=inst_es.path, output=logfile,)
3004
  if result.failed:
3005
    logging.error("External storage's %s command '%s' returned"
3006
                  " error: %s, logfile: %s, output: %s",
3007
                  action, result.cmd, result.fail_reason,
3008
                  logfile, result.output)
3009

  
3010
    # If logfile is 'None' (during attach), it breaks TailFile
3011
    # TODO: have a log file for attach too
3012
    if action is not constants.ES_ACTION_ATTACH:
3013
      lines = [utils.SafeEncode(val)
3014
               for val in utils.TailFile(logfile, lines=20)]
3015
    else:
3016
      lines = result.output[-20:]
3017

  
3018
    _ThrowError("External storage's %s script failed (%s), last"
3019
                " lines of output:\n%s",
3020
                action, result.fail_reason, "\n".join(lines))
3021

  
3022
  if action == constants.ES_ACTION_ATTACH:
3023
    return result.stdout
3024

  
3025

  
3026
def ExtStorageFromDisk(name, base_dir=None):
3027
  """Create an ExtStorage instance from disk.
3028

  
3029
  This function will return an ExtStorage instance
3030
  if the given name is a valid ExtStorage name.
3031

  
3032
  @type base_dir: string
3033
  @keyword base_dir: Base directory containing ExtStorage installations.
3034
                     Defaults to a search in all the ES_SEARCH_PATH dirs.
3035
  @rtype: tuple
3036
  @return: True and the ExtStorage instance if we find a valid one, or
3037
      False and the diagnose message on error
3038

  
3039
  """
3040
  if base_dir is None:
3041
    es_base_dir = pathutils.ES_SEARCH_PATH
3042
  else:
3043
    es_base_dir = [base_dir]
3044

  
3045
  es_dir = utils.FindFile(name, es_base_dir, os.path.isdir)
3046

  
3047
  if es_dir is None:
3048
    return False, ("Directory for External Storage Provider %s not"
3049
                   " found in search path" % name)
3050

  
3051
  # ES Files dictionary, we will populate it with the absolute path
3052
  # names; if the value is True, then it is a required file, otherwise
3053
  # an optional one
3054
  es_files = dict.fromkeys(constants.ES_SCRIPTS, True)
3055

  
3056
  for filename in es_files:
3057
    es_files[filename] = utils.PathJoin(es_dir, filename)
3058

  
3059
    try:
3060
      st = os.stat(es_files[filename])
3061
    except EnvironmentError, err:
3062
      return False, ("File '%s' under path '%s' is missing (%s)" %
3063
                     (filename, es_dir, utils.ErrnoOrStr(err)))
3064

  
3065
    if not stat.S_ISREG(stat.S_IFMT(st.st_mode)):
3066
      return False, ("File '%s' under path '%s' is not a regular file" %
3067
                     (filename, es_dir))
3068

  
3069
    if filename in constants.ES_SCRIPTS:
3070
      if stat.S_IMODE(st.st_mode) & stat.S_IXUSR != stat.S_IXUSR:
3071
        return False, ("File '%s' under path '%s' is not executable" %
3072
                       (filename, es_dir))
3073

  
3074
  es_obj = \
3075
    objects.ExtStorage(name=name, path=es_dir,
3076
                       create_script=es_files[constants.ES_SCRIPT_CREATE],
3077
                       remove_script=es_files[constants.ES_SCRIPT_REMOVE],
3078
                       grow_script=es_files[constants.ES_SCRIPT_GROW],
3079
                       attach_script=es_files[constants.ES_SCRIPT_ATTACH],
3080
                       detach_script=es_files[constants.ES_SCRIPT_DETACH],
3081
                       setinfo_script=es_files[constants.ES_SCRIPT_SETINFO])
3082
  return True, es_obj
3083

  
3084

  
3085
def _ExtStorageEnvironment(unique_id, size=None, grow=None, metadata=None):
3086
  """Calculate the environment for an External Storage script.
3087

  
3088
  @type unique_id: tuple (driver, vol_name)
3089
  @param unique_id: ExtStorage pool and name of the Volume
3090
  @type size: string
3091
  @param size: size of the Volume (in mebibytes)
3092
  @type grow: string
3093
  @param grow: new size of Volume after grow (in mebibytes)
3094
  @type metadata: string
3095
  @param metadata: metadata info of the Volume
3096
  @rtype: dict
3097
  @return: dict of environment variables
3098

  
3099
  """
3100
  vol_name = unique_id[1]
3101

  
3102
  result = {}
3103
  result["VOL_NAME"] = vol_name
3104

  
3105
  if size is not None:
3106
    result["VOL_SIZE"] = size
3107

  
3108
  if grow is not None:
3109
    result["VOL_NEW_SIZE"] = grow
3110

  
3111
  if metadata is not None:
3112
    result["VOL_METADATA"] = metadata
3113

  
3114
  return result
3115

  
3116

  
3117
def _VolumeLogName(kind, es_name, volume):
3118
  """Compute the ExtStorage log filename for a given Volume and operation.
3119

  
3120
  @type kind: string
3121
  @param kind: the operation type (e.g. create, remove etc.)
3122
  @type es_name: string
3123
  @param es_name: the ExtStorage name
3124
  @type volume: string
3125
  @param volume: the name of the Volume inside the External Storage
3126

  
3127
  """
3128
  # Check if the extstorage log dir is a valid dir
3129
  if not os.path.isdir(pathutils.LOG_ES_DIR):
3130
    _ThrowError("Cannot find log directory: %s", pathutils.LOG_ES_DIR)
3131

  
3132
  # TODO: Use tempfile.mkstemp to create unique filename
3133
  base = ("%s-%s-%s-%s.log" %
3134
          (kind, es_name, volume, utils.TimestampForFilename()))
3135
  return utils.PathJoin(pathutils.LOG_ES_DIR, base)
3136

  
3137

  
2789 3138
DEV_MAP = {
2790 3139
  constants.LD_LV: LogicalVolume,
2791 3140
  constants.LD_DRBD8: DRBD8,
2792 3141
  constants.LD_BLOCKDEV: PersistentBlockDevice,
2793 3142
  constants.LD_RBD: RADOSBlockDevice,
3143
  constants.LD_EXT: ExtStorageDevice,
2794 3144
  }
2795 3145

  
2796 3146
if constants.ENABLE_FILE_STORAGE or constants.ENABLE_SHARED_FILE_STORAGE:
b/lib/client/gnt_cluster.py
452 452
  ToStdout("  - primary ip version: %d", result["primary_ip_version"])
453 453
  ToStdout("  - preallocation wipe disks: %s", result["prealloc_wipe_disks"])
454 454
  ToStdout("  - OS search path: %s", utils.CommaJoin(pathutils.OS_SEARCH_PATH))
455
  ToStdout("  - ExtStorage Providers search path: %s",
456
           utils.CommaJoin(pathutils.ES_SEARCH_PATH))
455 457

  
456 458
  ToStdout("Default node parameters:")
457 459
  _PrintGroupedParams(result["ndparams"], roman=opts.roman_integers)
b/lib/cmdlib.py
8849 8849
      self._GoReconnect(False)
8850 8850
      self._WaitUntilSync()
8851 8851

  
8852
    # If the instance's disk template is `rbd' and there was a successful
8853
    # migration, unmap the device from the source node.
8854
    if self.instance.disk_template == constants.DT_RBD:
8852
    # If the instance's disk template is `rbd' or `ext' and there was a
8853
    # successful migration, unmap the device from the source node.
8854
    if self.instance.disk_template in (constants.DT_RBD, constants.DT_EXT):
8855 8855
      disks = _ExpandCheckDisks(instance, instance.disks)
8856 8856
      self.feedback_fn("* unmapping instance's disks from %s" % source_node)
8857 8857
      for disk in disks:
......
9101 9101
_DISK_TEMPLATE_NAME_PREFIX = {
9102 9102
  constants.DT_PLAIN: "",
9103 9103
  constants.DT_RBD: ".rbd",
9104
  constants.DT_EXT: ".ext",
9104 9105
  }
9105 9106

  
9106 9107

  
......
9110 9111
  constants.DT_SHARED_FILE: constants.LD_FILE,
9111 9112
  constants.DT_BLOCK: constants.LD_BLOCKDEV,
9112 9113
  constants.DT_RBD: constants.LD_RBD,
9114
  constants.DT_EXT: constants.LD_EXT,
9113 9115
  }
9114 9116

  
9115 9117

  
......
9189 9191
                                       disk[constants.IDISK_ADOPT])
9190 9192
    elif template_name == constants.DT_RBD:
9191 9193
      logical_id_fn = lambda idx, _, disk: ("rbd", names[idx])
9194
    elif template_name == constants.DT_EXT:
9195
      logical_id_fn = lambda idx, _, disk: ("ext", names[idx])
9192 9196
    else:
9193 9197
      raise errors.ProgrammerError("Unknown disk template '%s'" % template_name)
9194 9198

  
......
10433 10437
        # Any function that checks prerequisites can be placed here.
10434 10438
        # Check if there is enough space on the RADOS cluster.
10435 10439
        _CheckRADOSFreeSpace()
10440
      elif self.op.disk_template == constants.DT_EXT:
10441
        # FIXME: Function that checks prereqs if needed
10442
        pass
10436 10443
      else:
10437 10444
        # Check lv size requirements, if not adopting
10438 10445
        req_sizes = _ComputeDiskSizePerVG(self.op.disk_template, self.disks)
......
12318 12325

  
12319 12326
    if instance.disk_template not in (constants.DT_FILE,
12320 12327
                                      constants.DT_SHARED_FILE,
12321
                                      constants.DT_RBD):
12328
                                      constants.DT_RBD,
12329
                                      constants.DT_EXT):
12322 12330
      # TODO: check the free disk space for file, when that feature will be
12323 12331
      # supported
12324 12332
      _CheckNodesFreeDiskPerVG(self, nodenames,
b/lib/constants.py
369 369
DT_SHARED_FILE = "sharedfile"
370 370
DT_BLOCK = "blockdev"
371 371
DT_RBD = "rbd"
372
DT_EXT = "ext"
372 373

  
373 374
# the set of network-mirrored disk templates
374 375
DTS_INT_MIRROR = compat.UniqueFrozenset([DT_DRBD8])
......
378 379
  DT_SHARED_FILE,
379 380
  DT_BLOCK,
380 381
  DT_RBD,
382
  DT_EXT,
381 383
  ])
382 384

  
383 385
# the set of non-lvm-based disk templates
......
387 389
  DT_SHARED_FILE,
388 390
  DT_BLOCK,
389 391
  DT_RBD,
392
  DT_EXT,
390 393
  ])
391 394

  
392 395
# the set of disk templates which can be grown
......
396 399
  DT_FILE,
397 400
  DT_SHARED_FILE,
398 401
  DT_RBD,
402
  DT_EXT,
399 403
  ])
400 404

  
401 405
# the set of disk templates that allow adoption
......
422 426
LD_FILE = "file"
423 427
LD_BLOCKDEV = "blockdev"
424 428
LD_RBD = "rbd"
429
LD_EXT = "ext"
425 430
LOGICAL_DISK_TYPES = compat.UniqueFrozenset([
426 431
  LD_LV,
427 432
  LD_DRBD8,
428 433
  LD_FILE,
429 434
  LD_BLOCKDEV,
430 435
  LD_RBD,
436
  LD_EXT,
431 437
  ])
432 438

  
433 439
LDS_BLOCK = compat.UniqueFrozenset([
......
435 441
  LD_DRBD8,
436 442
  LD_BLOCKDEV,
437 443
  LD_RBD,
444
  LD_EXT,
438 445
  ])
439 446

  
440 447
# drbd constants
......
535 542
  DT_SHARED_FILE,
536 543
  DT_BLOCK,
537 544
  DT_RBD,
545
  DT_EXT
538 546
  ])
539 547

  
540 548
FILE_DRIVER = compat.UniqueFrozenset([FD_LOOP, FD_BLKTAP])
......
661 669
OS_VALIDATE_PARAMETERS = "parameters"
662 670
OS_VALIDATE_CALLS = compat.UniqueFrozenset([OS_VALIDATE_PARAMETERS])
663 671

  
672
# External Storage (ES) related constants
673
ES_ACTION_CREATE = "create"
674
ES_ACTION_REMOVE = "remove"
675
ES_ACTION_GROW = "grow"
676
ES_ACTION_ATTACH = "attach"
677
ES_ACTION_DETACH = "detach"
678
ES_ACTION_SETINFO = "setinfo"
679

  
680
ES_SCRIPT_CREATE = ES_ACTION_CREATE
681
ES_SCRIPT_REMOVE = ES_ACTION_REMOVE
682
ES_SCRIPT_GROW = ES_ACTION_GROW
683
ES_SCRIPT_ATTACH = ES_ACTION_ATTACH
684
ES_SCRIPT_DETACH = ES_ACTION_DETACH
685
ES_SCRIPT_SETINFO = ES_ACTION_SETINFO
686
ES_SCRIPTS = frozenset([
687
  ES_SCRIPT_CREATE,
688
  ES_SCRIPT_REMOVE,
689
  ES_SCRIPT_GROW,
690
  ES_SCRIPT_ATTACH,
691
  ES_SCRIPT_DETACH,
692
  ES_SCRIPT_SETINFO
693
  ])
694

  
664 695
# ssh constants
665 696
SSH = "ssh"
666 697
SCP = "scp"
......
1936 1967
  LD_RBD: {
1937 1968
    LDP_POOL: "rbd"
1938 1969
    },
1970
  LD_EXT: {},
1939 1971
  }
1940 1972

  
1941 1973
# readability shortcuts
......
1969 2001
  DT_RBD: {
1970 2002
    RBD_POOL: DISK_LD_DEFAULTS[LD_RBD][LDP_POOL]
1971 2003
    },
2004
  DT_EXT: {},
1972 2005
  }
1973 2006

  
1974 2007
# we don't want to export the shortcuts
b/lib/masterd/instance.py
1630 1630
    constants.DT_SHARED_FILE: sum(d[constants.IDISK_SIZE] for d in disks),
1631 1631
    constants.DT_BLOCK: 0,
1632 1632
    constants.DT_RBD: sum(d[constants.IDISK_SIZE] for d in disks),
1633
    constants.DT_EXT: sum(d[constants.IDISK_SIZE] for d in disks),
1633 1634
  }
1634 1635

  
1635 1636
  if disk_template not in req_size_dict:
b/lib/objects.py
602 602

  
603 603
    """
604 604
    if self.dev_type in [constants.LD_LV, constants.LD_FILE,
605
                         constants.LD_BLOCKDEV, constants.LD_RBD]:
605
                         constants.LD_BLOCKDEV, constants.LD_RBD,
606
                         constants.LD_EXT]:
606 607
      result = [node]
607 608
    elif self.dev_type in constants.LDS_DRBD:
608 609
      result = [self.logical_id[0], self.logical_id[1]]
......
678 679

  
679 680
    """
680 681
    if self.dev_type in (constants.LD_LV, constants.LD_FILE,
681
                         constants.LD_RBD):
682
                         constants.LD_RBD, constants.LD_EXT):
682 683
      self.size += amount
683 684
    elif self.dev_type == constants.LD_DRBD8:
684 685
      if self.children:
......
1235 1236
    return cls.SplitNameVariant(name)[1]
1236 1237

  
1237 1238

  
1239
class ExtStorage(ConfigObject):
1240
  """Config object representing an External Storage Provider.
1241

  
1242
  """
1243
  __slots__ = [
1244
    "name",
1245
    "path",
1246
    "create_script",
1247
    "remove_script",
1248
    "grow_script",
1249
    "attach_script",
1250
    "detach_script",
1251
    "setinfo_script",
1252
    ]
1253

  
1254

  
1238 1255
class NodeHvState(ConfigObject):
1239 1256
  """Hypvervisor state on a node.
1240 1257

  
b/lib/pathutils.py
34 34
  vcluster.AddNodePrefix(_autoconf.SHARED_FILE_STORAGE_DIR)
35 35
EXPORT_DIR = vcluster.AddNodePrefix(_autoconf.EXPORT_DIR)
36 36
OS_SEARCH_PATH = _autoconf.OS_SEARCH_PATH
37
ES_SEARCH_PATH = _autoconf.ES_SEARCH_PATH
37 38
SSH_CONFIG_DIR = _autoconf.SSH_CONFIG_DIR
38 39
SYSCONFDIR = vcluster.AddNodePrefix(_autoconf.SYSCONFDIR)
39 40
TOOLSDIR = _autoconf.TOOLSDIR
......
123 124
QUERY_SOCKET = SOCKET_DIR + "/ganeti-query"
124 125

  
125 126
LOG_OS_DIR = LOG_DIR + "/os"
127
LOG_ES_DIR = LOG_DIR + "/extstorage"
126 128

  
127 129
# Job queue paths
128 130
JOB_QUEUE_LOCK_FILE = QUEUE_DIR + "/lock"
b/tools/burnin
466 466
                                constants.DT_PLAIN,
467 467
                                constants.DT_DRBD8,
468 468
                                constants.DT_RBD,
469
                                constants.DT_EXT,
469 470
                                )
470 471
    if options.disk_template not in supported_disk_templates:
471 472
      Err("Unknown disk template '%s'" % options.disk_template)

Also available in: Unified diff