Revision 2656b017 lib/storage/filestorage.py

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