Revision 2656b017

b/lib/storage/bdev.py
24 24
"""
25 25

  
26 26
import re
27
import errno
28 27
import stat
29 28
import os
30 29
import logging
......
39 38
from ganeti import serializer
40 39
from ganeti.storage import base
41 40
from ganeti.storage import drbd
42
from ganeti.storage import filestorage
41
from ganeti.storage.filestorage import FileStorage
43 42

  
44 43

  
45 44
class RbdShowmappedJsonError(Exception):
......
714 713
    return len(self.pv_names)
715 714

  
716 715

  
717
class FileStorage(base.BlockDev):
718
  """File device.
719

  
720
  This class represents a file storage backend device.
721

  
722
  The unique_id for the file device is a (file_driver, file_path) tuple.
723

  
724
  """
725
  def __init__(self, unique_id, children, size, params, dyn_params):
726
    """Initalizes a file device backend.
727

  
728
    """
729
    if children:
730
      raise errors.BlockDeviceError("Invalid setup for file device")
731
    super(FileStorage, self).__init__(unique_id, children, size, params,
732
                                      dyn_params)
733
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
734
      raise ValueError("Invalid configuration data %s" % str(unique_id))
735
    self.driver = unique_id[0]
736
    self.dev_path = unique_id[1]
737

  
738
    filestorage.CheckFileStoragePathAcceptance(self.dev_path)
739

  
740
    self.Attach()
741

  
742
  def Assemble(self):
743
    """Assemble the device.
744

  
745
    Checks whether the file device exists, raises BlockDeviceError otherwise.
746

  
747
    """
748
    if not os.path.exists(self.dev_path):
749
      base.ThrowError("File device '%s' does not exist" % self.dev_path)
750

  
751
  def Shutdown(self):
752
    """Shutdown the device.
753

  
754
    This is a no-op for the file type, as we don't deactivate
755
    the file on shutdown.
756

  
757
    """
758
    pass
759

  
760
  def Open(self, force=False):
761
    """Make the device ready for I/O.
762

  
763
    This is a no-op for the file type.
764

  
765
    """
766
    pass
767

  
768
  def Close(self):
769
    """Notifies that the device will no longer be used for I/O.
770

  
771
    This is a no-op for the file type.
772

  
773
    """
774
    pass
775

  
776
  def Remove(self):
777
    """Remove the file backing the block device.
778

  
779
    @rtype: boolean
780
    @return: True if the removal was successful
781

  
782
    """
783
    try:
784
      os.remove(self.dev_path)
785
    except OSError, err:
786
      if err.errno != errno.ENOENT:
787
        base.ThrowError("Can't remove file '%s': %s", self.dev_path, err)
788

  
789
  def Rename(self, new_id):
790
    """Renames the file.
791

  
792
    """
793
    # TODO: implement rename for file-based storage
794
    base.ThrowError("Rename is not supported for file-based storage")
795

  
796
  def Grow(self, amount, dryrun, backingstore, excl_stor):
797
    """Grow the file
798

  
799
    @param amount: the amount (in mebibytes) to grow with
800

  
801
    """
802
    if not backingstore:
803
      return
804
    # Check that the file exists
805
    self.Assemble()
806
    current_size = self.GetActualSize()
807
    new_size = current_size + amount * 1024 * 1024
808
    assert new_size > current_size, "Cannot Grow with a negative amount"
809
    # We can't really simulate the growth
810
    if dryrun:
811
      return
812
    try:
813
      f = open(self.dev_path, "a+")
814
      f.truncate(new_size)
815
      f.close()
816
    except EnvironmentError, err:
817
      base.ThrowError("Error in file growth: %", str(err))
818

  
819
  def Attach(self):
820
    """Attach to an existing file.
821

  
822
    Check if this file already exists.
823

  
824
    @rtype: boolean
825
    @return: True if file exists
826

  
827
    """
828
    self.attached = os.path.exists(self.dev_path)
829
    return self.attached
830

  
831
  def GetActualSize(self):
832
    """Return the actual disk size.
833

  
834
    @note: the device needs to be active when this is called
835

  
836
    """
837
    assert self.attached, "BlockDevice not attached in GetActualSize()"
838
    try:
839
      st = os.stat(self.dev_path)
840
      return st.st_size
841
    except OSError, err:
842
      base.ThrowError("Can't stat %s: %s", self.dev_path, err)
843

  
844
  @classmethod
845
  def Create(cls, unique_id, children, size, spindles, params, excl_stor,
846
             dyn_params):
847
    """Create a new file.
848

  
849
    @param size: the size of file in MiB
850

  
851
    @rtype: L{bdev.FileStorage}
852
    @return: an instance of FileStorage
853

  
854
    """
855
    if excl_stor:
856
      raise errors.ProgrammerError("FileStorage device requested with"
857
                                   " exclusive_storage")
858
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
859
      raise ValueError("Invalid configuration data %s" % str(unique_id))
860

  
861
    dev_path = unique_id[1]
862

  
863
    filestorage.CheckFileStoragePathAcceptance(dev_path)
864

  
865
    try:
866
      fd = os.open(dev_path, os.O_RDWR | os.O_CREAT | os.O_EXCL)
867
      f = os.fdopen(fd, "w")
868
      f.truncate(size * 1024 * 1024)
869
      f.close()
870
    except EnvironmentError, err:
871
      if err.errno == errno.EEXIST:
872
        base.ThrowError("File already existing: %s", dev_path)
873
      base.ThrowError("Error in file creation: %", str(err))
874

  
875
    return FileStorage(unique_id, children, size, params, dyn_params)
876

  
877

  
878 716
class PersistentBlockDevice(base.BlockDev):
879 717
  """A block device with persistent node
880 718

  
b/lib/storage/filestorage.py
19 19
# 02110-1301, USA.
20 20

  
21 21

  
22
"""File storage functions.
22
"""Filesystem-based access functions and disk templates.
23 23

  
24 24
"""
25 25

  
26 26
import logging
27
import errno
27 28
import os
28 29

  
29 30
from ganeti import compat
......
31 32
from ganeti import errors
32 33
from ganeti import pathutils
33 34
from ganeti import utils
35
from ganeti.storage import base
36

  
37

  
38
class FileStorage(base.BlockDev):
39
  """File device.
40

  
41
  This class represents a file storage backend device.
42

  
43
  The unique_id for the file device is a (file_driver, file_path) tuple.
44

  
45
  """
46
  def __init__(self, unique_id, children, size, params, dyn_params):
47
    """Initalizes a file device backend.
48

  
49
    """
50
    if children:
51
      raise errors.BlockDeviceError("Invalid setup for file device")
52
    super(FileStorage, self).__init__(unique_id, children, size, params,
53
                                      dyn_params)
54
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
55
      raise ValueError("Invalid configuration data %s" % str(unique_id))
56
    self.driver = unique_id[0]
57
    self.dev_path = unique_id[1]
58

  
59
    CheckFileStoragePathAcceptance(self.dev_path)
60

  
61
    self.Attach()
62

  
63
  def Assemble(self):
64
    """Assemble the device.
65

  
66
    Checks whether the file device exists, raises BlockDeviceError otherwise.
67

  
68
    """
69
    if not os.path.exists(self.dev_path):
70
      base.ThrowError("File device '%s' does not exist" % self.dev_path)
71

  
72
  def Shutdown(self):
73
    """Shutdown the device.
74

  
75
    This is a no-op for the file type, as we don't deactivate
76
    the file on shutdown.
77

  
78
    """
79
    pass
80

  
81
  def Open(self, force=False):
82
    """Make the device ready for I/O.
83

  
84
    This is a no-op for the file type.
85

  
86
    """
87
    pass
88

  
89
  def Close(self):
90
    """Notifies that the device will no longer be used for I/O.
91

  
92
    This is a no-op for the file type.
93

  
94
    """
95
    pass
96

  
97
  def Remove(self):
98
    """Remove the file backing the block device.
99

  
100
    @rtype: boolean
101
    @return: True if the removal was successful
102

  
103
    """
104
    try:
105
      os.remove(self.dev_path)
106
    except OSError, err:
107
      if err.errno != errno.ENOENT:
108
        base.ThrowError("Can't remove file '%s': %s", self.dev_path, err)
109

  
110
  def Rename(self, new_id):
111
    """Renames the file.
112

  
113
    """
114
    # TODO: implement rename for file-based storage
115
    base.ThrowError("Rename is not supported for file-based storage")
116

  
117
  def Grow(self, amount, dryrun, backingstore, excl_stor):
118
    """Grow the file
119

  
120
    @param amount: the amount (in mebibytes) to grow with
121

  
122
    """
123
    if not backingstore:
124
      return
125
    # Check that the file exists
126
    self.Assemble()
127
    current_size = self.GetActualSize()
128
    new_size = current_size + amount * 1024 * 1024
129
    assert new_size > current_size, "Cannot Grow with a negative amount"
130
    # We can't really simulate the growth
131
    if dryrun:
132
      return
133
    try:
134
      f = open(self.dev_path, "a+")
135
      f.truncate(new_size)
136
      f.close()
137
    except EnvironmentError, err:
138
      base.ThrowError("Error in file growth: %", str(err))
139

  
140
  def Attach(self):
141
    """Attach to an existing file.
142

  
143
    Check if this file already exists.
144

  
145
    @rtype: boolean
146
    @return: True if file exists
147

  
148
    """
149
    self.attached = os.path.exists(self.dev_path)
150
    return self.attached
151

  
152
  def GetActualSize(self):
153
    """Return the actual disk size.
154

  
155
    @note: the device needs to be active when this is called
156

  
157
    """
158
    assert self.attached, "BlockDevice not attached in GetActualSize()"
159
    try:
160
      st = os.stat(self.dev_path)
161
      return st.st_size
162
    except OSError, err:
163
      base.ThrowError("Can't stat %s: %s", self.dev_path, err)
164

  
165
  @classmethod
166
  def Create(cls, unique_id, children, size, spindles, params, excl_stor,
167
             dyn_params):
168
    """Create a new file.
169

  
170
    @type size: int
171
    @param size: the size of file in MiB
172

  
173
    @rtype: L{bdev.FileStorage}
174
    @return: an instance of FileStorage
175

  
176
    """
177
    if excl_stor:
178
      raise errors.ProgrammerError("FileStorage device requested with"
179
                                   " exclusive_storage")
180
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
181
      raise ValueError("Invalid configuration data %s" % str(unique_id))
182

  
183
    dev_path = unique_id[1]
184

  
185
    CheckFileStoragePathAcceptance(dev_path)
186

  
187
    try:
188
      fd = os.open(dev_path, os.O_RDWR | os.O_CREAT | os.O_EXCL)
189
      f = os.fdopen(fd, "w")
190
      f.truncate(size * 1024 * 1024)
191
      f.close()
192
    except EnvironmentError, err:
193
      if err.errno == errno.EEXIST:
194
        base.ThrowError("File already existing: %s", dev_path)
195
      base.ThrowError("Error in file creation: %", str(err))
196

  
197
    return FileStorage(unique_id, children, size, params, dyn_params)
34 198

  
35 199

  
36 200
def GetFileStorageSpaceInfo(path):

Also available in: Unified diff