Statistics
| Branch: | Tag: | Revision:

root / lib / storage / gluster.py @ 653bc0f1

History | View | Annotate | Download (13.2 kB)

1 8106dd64 Santi Raffa
#
2 8106dd64 Santi Raffa
#
3 8106dd64 Santi Raffa
4 8106dd64 Santi Raffa
# Copyright (C) 2013 Google Inc.
5 8106dd64 Santi Raffa
#
6 8106dd64 Santi Raffa
# This program is free software; you can redistribute it and/or modify
7 8106dd64 Santi Raffa
# it under the terms of the GNU General Public License as published by
8 8106dd64 Santi Raffa
# the Free Software Foundation; either version 2 of the License, or
9 8106dd64 Santi Raffa
# (at your option) any later version.
10 8106dd64 Santi Raffa
#
11 8106dd64 Santi Raffa
# This program is distributed in the hope that it will be useful, but
12 8106dd64 Santi Raffa
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 8106dd64 Santi Raffa
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 8106dd64 Santi Raffa
# General Public License for more details.
15 8106dd64 Santi Raffa
#
16 8106dd64 Santi Raffa
# You should have received a copy of the GNU General Public License
17 8106dd64 Santi Raffa
# along with this program; if not, write to the Free Software
18 8106dd64 Santi Raffa
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 8106dd64 Santi Raffa
# 02110-1301, USA.
20 8106dd64 Santi Raffa
21 8106dd64 Santi Raffa
"""Gluster storage class.
22 8106dd64 Santi Raffa

23 8106dd64 Santi Raffa
This class is very similar to FileStorage, given that Gluster when mounted
24 8106dd64 Santi Raffa
behaves essentially like a regular file system. Unlike RBD, there are no
25 8106dd64 Santi Raffa
special provisions for block device abstractions (yet).
26 8106dd64 Santi Raffa

27 8106dd64 Santi Raffa
"""
28 58793040 Santi Raffa
import logging
29 58793040 Santi Raffa
import os
30 58793040 Santi Raffa
import socket
31 58793040 Santi Raffa
32 58793040 Santi Raffa
from ganeti import utils
33 8106dd64 Santi Raffa
from ganeti import errors
34 58793040 Santi Raffa
from ganeti import netutils
35 58793040 Santi Raffa
from ganeti import constants
36 845b7ed1 Santi Raffa
from ganeti import ssconf
37 8106dd64 Santi Raffa
38 58793040 Santi Raffa
from ganeti.utils import io
39 8106dd64 Santi Raffa
from ganeti.storage import base
40 8106dd64 Santi Raffa
from ganeti.storage.filestorage import FileDeviceHelper
41 8106dd64 Santi Raffa
42 8106dd64 Santi Raffa
43 58793040 Santi Raffa
class GlusterVolume(object):
44 58793040 Santi Raffa
  """This class represents a Gluster volume.
45 58793040 Santi Raffa

46 58793040 Santi Raffa
  Volumes are uniquely identified by:
47 58793040 Santi Raffa

48 58793040 Santi Raffa
    - their IP address
49 58793040 Santi Raffa
    - their port
50 58793040 Santi Raffa
    - the volume name itself
51 58793040 Santi Raffa

52 58793040 Santi Raffa
  Two GlusterVolume objects x, y with same IP address, port and volume name
53 58793040 Santi Raffa
  are considered equal.
54 58793040 Santi Raffa

55 58793040 Santi Raffa
  """
56 58793040 Santi Raffa
57 ac156ecd Santi Raffa
  def __init__(self, server_addr, port, volume, _run_cmd=utils.RunCmd,
58 ac156ecd Santi Raffa
               _mount_point=None):
59 58793040 Santi Raffa
    """Creates a Gluster volume object.
60 58793040 Santi Raffa

61 58793040 Santi Raffa
    @type server_addr: str
62 58793040 Santi Raffa
    @param server_addr: The address to connect to
63 58793040 Santi Raffa

64 58793040 Santi Raffa
    @type port: int
65 58793040 Santi Raffa
    @param port: The port to connect to (Gluster standard is 24007)
66 58793040 Santi Raffa

67 58793040 Santi Raffa
    @type volume: str
68 58793040 Santi Raffa
    @param volume: The gluster volume to use for storage.
69 58793040 Santi Raffa

70 58793040 Santi Raffa
    """
71 58793040 Santi Raffa
    self.server_addr = server_addr
72 58793040 Santi Raffa
    server_ip = netutils.Hostname.GetIP(self.server_addr)
73 58793040 Santi Raffa
    self._server_ip = server_ip
74 58793040 Santi Raffa
    port = netutils.ValidatePortNumber(port)
75 58793040 Santi Raffa
    self._port = port
76 58793040 Santi Raffa
    self._volume = volume
77 ac156ecd Santi Raffa
    if _mount_point: # tests
78 ac156ecd Santi Raffa
      self.mount_point = _mount_point
79 ac156ecd Santi Raffa
    else:
80 ac156ecd Santi Raffa
      self.mount_point = ssconf.SimpleStore().GetGlusterStorageDir()
81 58793040 Santi Raffa
82 58793040 Santi Raffa
    self._run_cmd = _run_cmd
83 58793040 Santi Raffa
84 58793040 Santi Raffa
  @property
85 58793040 Santi Raffa
  def server_ip(self):
86 58793040 Santi Raffa
    return self._server_ip
87 58793040 Santi Raffa
88 58793040 Santi Raffa
  @property
89 58793040 Santi Raffa
  def port(self):
90 58793040 Santi Raffa
    return self._port
91 58793040 Santi Raffa
92 58793040 Santi Raffa
  @property
93 58793040 Santi Raffa
  def volume(self):
94 58793040 Santi Raffa
    return self._volume
95 58793040 Santi Raffa
96 58793040 Santi Raffa
  def __eq__(self, other):
97 58793040 Santi Raffa
    return (self.server_ip, self.port, self.volume) == \
98 58793040 Santi Raffa
           (other.server_ip, other.port, other.volume)
99 58793040 Santi Raffa
100 58793040 Santi Raffa
  def __repr__(self):
101 58793040 Santi Raffa
    return """GlusterVolume("{ip}", {port}, "{volume}")""" \
102 58793040 Santi Raffa
             .format(ip=self.server_ip, port=self.port, volume=self.volume)
103 58793040 Santi Raffa
104 58793040 Santi Raffa
  def __hash__(self):
105 58793040 Santi Raffa
    return (self.server_ip, self.port, self.volume).__hash__()
106 58793040 Santi Raffa
107 58793040 Santi Raffa
  def _IsMounted(self):
108 58793040 Santi Raffa
    """Checks if we are mounted or not.
109 58793040 Santi Raffa

110 58793040 Santi Raffa
    @rtype: bool
111 58793040 Santi Raffa
    @return: True if this volume is mounted.
112 58793040 Santi Raffa

113 58793040 Santi Raffa
    """
114 58793040 Santi Raffa
    if not os.path.exists(self.mount_point):
115 58793040 Santi Raffa
      return False
116 58793040 Santi Raffa
117 58793040 Santi Raffa
    return os.path.ismount(self.mount_point)
118 58793040 Santi Raffa
119 58793040 Santi Raffa
  def _GuessMountFailReasons(self):
120 58793040 Santi Raffa
    """Try and give reasons why the mount might've failed.
121 58793040 Santi Raffa

122 58793040 Santi Raffa
    @rtype: str
123 58793040 Santi Raffa
    @return: A semicolon-separated list of problems found with the current setup
124 58793040 Santi Raffa
             suitable for display to the user.
125 58793040 Santi Raffa

126 58793040 Santi Raffa
    """
127 58793040 Santi Raffa
128 58793040 Santi Raffa
    reasons = []
129 58793040 Santi Raffa
130 58793040 Santi Raffa
    # Does the mount point exist?
131 58793040 Santi Raffa
    if not os.path.exists(self.mount_point):
132 58793040 Santi Raffa
      reasons.append("%r: does not exist" % self.mount_point)
133 58793040 Santi Raffa
134 58793040 Santi Raffa
    # Okay, it exists, but is it a directory?
135 58793040 Santi Raffa
    elif not os.path.isdir(self.mount_point):
136 58793040 Santi Raffa
      reasons.append("%r: not a directory" % self.mount_point)
137 58793040 Santi Raffa
138 58793040 Santi Raffa
    # If, for some unfortunate reason, this folder exists before mounting:
139 58793040 Santi Raffa
    #
140 58793040 Santi Raffa
    #   /var/run/ganeti/gluster/gv0/10.0.0.1:30000:gv0/
141 58793040 Santi Raffa
    #   '--------- cwd ------------'
142 58793040 Santi Raffa
    #
143 58793040 Santi Raffa
    # and you _are_ trying to mount the gluster volume gv0 on 10.0.0.1:30000,
144 58793040 Santi Raffa
    # then the mount.glusterfs command parser gets confused and this command:
145 58793040 Santi Raffa
    #
146 58793040 Santi Raffa
    #   mount -t glusterfs 10.0.0.1:30000:gv0 /var/run/ganeti/gluster/gv0
147 58793040 Santi Raffa
    #                      '-- remote end --' '------ mountpoint -------'
148 58793040 Santi Raffa
    #
149 58793040 Santi Raffa
    # gets parsed instead like this:
150 58793040 Santi Raffa
    #
151 58793040 Santi Raffa
    #   mount -t glusterfs 10.0.0.1:30000:gv0 /var/run/ganeti/gluster/gv0
152 58793040 Santi Raffa
    #                      '-- mountpoint --' '----- syntax error ------'
153 58793040 Santi Raffa
    #
154 58793040 Santi Raffa
    # and if there _is_ a gluster server running locally at the default remote
155 58793040 Santi Raffa
    # end, localhost:24007, then this is not a network error and therefore... no
156 58793040 Santi Raffa
    # usage message gets printed out. All you get is a Byson parser error in the
157 58793040 Santi Raffa
    # gluster log files about an unexpected token in line 1, "". (That's stdin.)
158 58793040 Santi Raffa
    #
159 58793040 Santi Raffa
    # Not that we rely on that output in any way whatsoever...
160 58793040 Santi Raffa
161 58793040 Santi Raffa
    parser_confusing = io.PathJoin(self.mount_point,
162 58793040 Santi Raffa
                                   self._GetFUSEMountString())
163 58793040 Santi Raffa
    if os.path.exists(parser_confusing):
164 58793040 Santi Raffa
      reasons.append("%r: please delete, rename or move." % parser_confusing)
165 58793040 Santi Raffa
166 58793040 Santi Raffa
    # Let's try something else: can we connect to the server?
167 58793040 Santi Raffa
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
168 58793040 Santi Raffa
    try:
169 58793040 Santi Raffa
      sock.connect((self.server_ip, self.port))
170 58793040 Santi Raffa
      sock.close()
171 58793040 Santi Raffa
    except socket.error as err:
172 58793040 Santi Raffa
      reasons.append("%s:%d: %s" % (self.server_ip, self.port, err.strerror))
173 58793040 Santi Raffa
174 58793040 Santi Raffa
    reasons.append("try running 'gluster volume info %s' on %s to ensure"
175 58793040 Santi Raffa
                   " it exists, it is started and it is using the tcp"
176 58793040 Santi Raffa
                   " transport" % (self.volume, self.server_ip))
177 58793040 Santi Raffa
178 58793040 Santi Raffa
    return "; ".join(reasons)
179 58793040 Santi Raffa
180 58793040 Santi Raffa
  def _GetFUSEMountString(self):
181 58793040 Santi Raffa
    """Return the string FUSE needs to mount this volume.
182 58793040 Santi Raffa

183 58793040 Santi Raffa
    @rtype: str
184 58793040 Santi Raffa
    """
185 58793040 Santi Raffa
186 58793040 Santi Raffa
    return "{ip}:{port}:{volume}" \
187 58793040 Santi Raffa
              .format(ip=self.server_ip, port=self.port, volume=self.volume)
188 58793040 Santi Raffa
189 58793040 Santi Raffa
  def GetKVMMountString(self, path):
190 58793040 Santi Raffa
    """Return the string KVM needs to use this volume.
191 58793040 Santi Raffa

192 58793040 Santi Raffa
    @rtype: str
193 58793040 Santi Raffa
    """
194 58793040 Santi Raffa
195 58793040 Santi Raffa
    ip = self.server_ip
196 58793040 Santi Raffa
    if netutils.IPAddress.GetAddressFamily(ip) == socket.AF_INET6:
197 58793040 Santi Raffa
      ip = "[%s]" % ip
198 58793040 Santi Raffa
    return "gluster://{ip}:{port}/{volume}/{path}" \
199 58793040 Santi Raffa
              .format(ip=ip, port=self.port, volume=self.volume, path=path)
200 58793040 Santi Raffa
201 58793040 Santi Raffa
  def Mount(self):
202 58793040 Santi Raffa
    """Try and mount the volume. No-op if the volume is already mounted.
203 58793040 Santi Raffa

204 58793040 Santi Raffa
    @raises BlockDeviceError: if the mount was unsuccessful
205 58793040 Santi Raffa

206 58793040 Santi Raffa
    @rtype: context manager
207 58793040 Santi Raffa
    @return: A simple context manager that lets you use this volume for
208 58793040 Santi Raffa
             short lived operations like so::
209 58793040 Santi Raffa

210 58793040 Santi Raffa
              with volume.mount():
211 58793040 Santi Raffa
                # Do operations on volume
212 58793040 Santi Raffa
              # Volume is now unmounted
213 58793040 Santi Raffa

214 58793040 Santi Raffa
    """
215 58793040 Santi Raffa
216 58793040 Santi Raffa
    class _GlusterVolumeContextManager(object):
217 58793040 Santi Raffa
218 58793040 Santi Raffa
      def __init__(self, volume):
219 58793040 Santi Raffa
        self.volume = volume
220 58793040 Santi Raffa
221 58793040 Santi Raffa
      def __enter__(self):
222 58793040 Santi Raffa
        # We're already mounted.
223 58793040 Santi Raffa
        return self
224 58793040 Santi Raffa
225 58793040 Santi Raffa
      def __exit__(self, *exception_information):
226 58793040 Santi Raffa
        self.volume.Unmount()
227 58793040 Santi Raffa
        return False # do not swallow exceptions.
228 58793040 Santi Raffa
229 58793040 Santi Raffa
    if self._IsMounted():
230 58793040 Santi Raffa
      return _GlusterVolumeContextManager(self)
231 58793040 Santi Raffa
232 58793040 Santi Raffa
    command = ["mount",
233 58793040 Santi Raffa
               "-t", "glusterfs",
234 58793040 Santi Raffa
               self._GetFUSEMountString(),
235 58793040 Santi Raffa
               self.mount_point]
236 58793040 Santi Raffa
237 58793040 Santi Raffa
    io.Makedirs(self.mount_point)
238 58793040 Santi Raffa
    self._run_cmd(" ".join(command),
239 58793040 Santi Raffa
                  # Why set cwd? Because it's an area we control. If,
240 58793040 Santi Raffa
                  # for some unfortunate reason, this folder exists:
241 58793040 Santi Raffa
                  #   "/%s/" % _GetFUSEMountString()
242 58793040 Santi Raffa
                  # ...then the gluster parser gets confused and treats
243 58793040 Santi Raffa
                  # _GetFUSEMountString() as your mount point and
244 58793040 Santi Raffa
                  # self.mount_point becomes a syntax error.
245 58793040 Santi Raffa
                  cwd=self.mount_point)
246 58793040 Santi Raffa
247 58793040 Santi Raffa
    # mount.glusterfs exits with code 0 even after failure.
248 58793040 Santi Raffa
    # https://bugzilla.redhat.com/show_bug.cgi?id=1031973
249 58793040 Santi Raffa
    if not self._IsMounted():
250 58793040 Santi Raffa
      reasons = self._GuessMountFailReasons()
251 58793040 Santi Raffa
      if not reasons:
252 58793040 Santi Raffa
        reasons = "%r failed." % (" ".join(command))
253 58793040 Santi Raffa
      base.ThrowError("%r: mount failure: %s",
254 58793040 Santi Raffa
                      self.mount_point,
255 58793040 Santi Raffa
                      reasons)
256 58793040 Santi Raffa
257 58793040 Santi Raffa
    return _GlusterVolumeContextManager(self)
258 58793040 Santi Raffa
259 58793040 Santi Raffa
  def Unmount(self):
260 58793040 Santi Raffa
    """Try and unmount the volume.
261 58793040 Santi Raffa

262 58793040 Santi Raffa
    Failures are logged but otherwise ignored.
263 58793040 Santi Raffa

264 58793040 Santi Raffa
    @raises BlockDeviceError: if the volume was not mounted to begin with.
265 58793040 Santi Raffa
    """
266 58793040 Santi Raffa
267 58793040 Santi Raffa
    if not self._IsMounted():
268 58793040 Santi Raffa
      base.ThrowError("%r: should be mounted but isn't.", self.mount_point)
269 58793040 Santi Raffa
270 58793040 Santi Raffa
    result = self._run_cmd(["umount",
271 58793040 Santi Raffa
                            self.mount_point])
272 58793040 Santi Raffa
273 58793040 Santi Raffa
    if result.failed:
274 58793040 Santi Raffa
      logging.warning("Failed to unmount %r from %r: %s",
275 58793040 Santi Raffa
                      self, self.mount_point, result.fail_reason)
276 58793040 Santi Raffa
277 58793040 Santi Raffa
278 8106dd64 Santi Raffa
class GlusterStorage(base.BlockDev):
279 8106dd64 Santi Raffa
  """File device using the Gluster backend.
280 8106dd64 Santi Raffa

281 845b7ed1 Santi Raffa
  This class represents a file storage backend device stored on Gluster. Ganeti
282 845b7ed1 Santi Raffa
  mounts and unmounts the Gluster devices automatically.
283 8106dd64 Santi Raffa

284 8106dd64 Santi Raffa
  The unique_id for the file device is a (file_driver, file_path) tuple.
285 8106dd64 Santi Raffa

286 8106dd64 Santi Raffa
  """
287 c032b2ce Klaus Aehlig
  def __init__(self, unique_id, children, size, params, dyn_params, *args):
288 8106dd64 Santi Raffa
    """Initalizes a file device backend.
289 8106dd64 Santi Raffa

290 8106dd64 Santi Raffa
    """
291 8106dd64 Santi Raffa
    if children:
292 8106dd64 Santi Raffa
      base.ThrowError("Invalid setup for file device")
293 8106dd64 Santi Raffa
294 845b7ed1 Santi Raffa
    try:
295 845b7ed1 Santi Raffa
      driver, path = unique_id
296 845b7ed1 Santi Raffa
    except ValueError: # wrong number of arguments
297 845b7ed1 Santi Raffa
      raise ValueError("Invalid configuration data %s" % repr(unique_id))
298 845b7ed1 Santi Raffa
299 845b7ed1 Santi Raffa
    server_addr = params[constants.GLUSTER_HOST]
300 845b7ed1 Santi Raffa
    port = params[constants.GLUSTER_PORT]
301 845b7ed1 Santi Raffa
    volume = params[constants.GLUSTER_VOLUME]
302 845b7ed1 Santi Raffa
303 845b7ed1 Santi Raffa
    self.volume = GlusterVolume(server_addr, port, volume)
304 845b7ed1 Santi Raffa
    self.path = path
305 845b7ed1 Santi Raffa
    self.driver = driver
306 845b7ed1 Santi Raffa
    self.full_path = io.PathJoin(self.volume.mount_point, self.path)
307 845b7ed1 Santi Raffa
    self.file = None
308 845b7ed1 Santi Raffa
309 845b7ed1 Santi Raffa
    super(GlusterStorage, self).__init__(unique_id, children, size,
310 c032b2ce Klaus Aehlig
                                         params, dyn_params, *args)
311 8106dd64 Santi Raffa
312 8106dd64 Santi Raffa
    self.Attach()
313 8106dd64 Santi Raffa
314 8106dd64 Santi Raffa
  def Assemble(self):
315 8106dd64 Santi Raffa
    """Assemble the device.
316 8106dd64 Santi Raffa

317 8106dd64 Santi Raffa
    Checks whether the file device exists, raises BlockDeviceError otherwise.
318 8106dd64 Santi Raffa

319 8106dd64 Santi Raffa
    """
320 8106dd64 Santi Raffa
    assert self.attached, "Gluster file assembled without being attached"
321 8106dd64 Santi Raffa
    self.file.Exists(assert_exists=True)
322 8106dd64 Santi Raffa
323 8106dd64 Santi Raffa
  def Shutdown(self):
324 8106dd64 Santi Raffa
    """Shutdown the device.
325 8106dd64 Santi Raffa

326 8106dd64 Santi Raffa
    """
327 8106dd64 Santi Raffa
328 8106dd64 Santi Raffa
    self.file = None
329 8106dd64 Santi Raffa
    self.dev_path = None
330 8106dd64 Santi Raffa
    self.attached = False
331 8106dd64 Santi Raffa
332 8106dd64 Santi Raffa
  def Open(self, force=False):
333 8106dd64 Santi Raffa
    """Make the device ready for I/O.
334 8106dd64 Santi Raffa

335 8106dd64 Santi Raffa
    This is a no-op for the file type.
336 8106dd64 Santi Raffa

337 8106dd64 Santi Raffa
    """
338 8106dd64 Santi Raffa
    assert self.attached, "Gluster file opened without being attached"
339 8106dd64 Santi Raffa
340 8106dd64 Santi Raffa
  def Close(self):
341 8106dd64 Santi Raffa
    """Notifies that the device will no longer be used for I/O.
342 8106dd64 Santi Raffa

343 8106dd64 Santi Raffa
    This is a no-op for the file type.
344 8106dd64 Santi Raffa
    """
345 8106dd64 Santi Raffa
    pass
346 8106dd64 Santi Raffa
347 8106dd64 Santi Raffa
  def Remove(self):
348 8106dd64 Santi Raffa
    """Remove the file backing the block device.
349 8106dd64 Santi Raffa

350 8106dd64 Santi Raffa
    @rtype: boolean
351 8106dd64 Santi Raffa
    @return: True if the removal was successful
352 8106dd64 Santi Raffa

353 8106dd64 Santi Raffa
    """
354 845b7ed1 Santi Raffa
    with self.volume.Mount():
355 845b7ed1 Santi Raffa
      self.file = FileDeviceHelper(self.full_path)
356 845b7ed1 Santi Raffa
      if self.file.Remove():
357 845b7ed1 Santi Raffa
        self.file = None
358 845b7ed1 Santi Raffa
        return True
359 845b7ed1 Santi Raffa
      else:
360 845b7ed1 Santi Raffa
        return False
361 8106dd64 Santi Raffa
362 8106dd64 Santi Raffa
  def Rename(self, new_id):
363 8106dd64 Santi Raffa
    """Renames the file.
364 8106dd64 Santi Raffa

365 8106dd64 Santi Raffa
    """
366 8106dd64 Santi Raffa
    # TODO: implement rename for file-based storage
367 8106dd64 Santi Raffa
    base.ThrowError("Rename is not supported for Gluster storage")
368 8106dd64 Santi Raffa
369 8106dd64 Santi Raffa
  def Grow(self, amount, dryrun, backingstore, excl_stor):
370 8106dd64 Santi Raffa
    """Grow the file
371 8106dd64 Santi Raffa

372 8106dd64 Santi Raffa
    @param amount: the amount (in mebibytes) to grow with
373 8106dd64 Santi Raffa

374 8106dd64 Santi Raffa
    """
375 8106dd64 Santi Raffa
    self.file.Grow(amount, dryrun, backingstore, excl_stor)
376 8106dd64 Santi Raffa
377 8106dd64 Santi Raffa
  def Attach(self):
378 8106dd64 Santi Raffa
    """Attach to an existing file.
379 8106dd64 Santi Raffa

380 8106dd64 Santi Raffa
    Check if this file already exists.
381 8106dd64 Santi Raffa

382 8106dd64 Santi Raffa
    @rtype: boolean
383 8106dd64 Santi Raffa
    @return: True if file exists
384 8106dd64 Santi Raffa

385 8106dd64 Santi Raffa
    """
386 845b7ed1 Santi Raffa
    try:
387 845b7ed1 Santi Raffa
      self.volume.Mount()
388 845b7ed1 Santi Raffa
      self.file = FileDeviceHelper(self.full_path)
389 845b7ed1 Santi Raffa
      self.dev_path = self.full_path
390 845b7ed1 Santi Raffa
    except Exception as err:
391 845b7ed1 Santi Raffa
      self.volume.Unmount()
392 845b7ed1 Santi Raffa
      raise err
393 845b7ed1 Santi Raffa
394 8106dd64 Santi Raffa
    self.attached = self.file.Exists()
395 8106dd64 Santi Raffa
    return self.attached
396 8106dd64 Santi Raffa
397 8106dd64 Santi Raffa
  def GetActualSize(self):
398 8106dd64 Santi Raffa
    """Return the actual disk size.
399 8106dd64 Santi Raffa

400 8106dd64 Santi Raffa
    @note: the device needs to be active when this is called
401 8106dd64 Santi Raffa

402 8106dd64 Santi Raffa
    """
403 8106dd64 Santi Raffa
    return self.file.Size()
404 8106dd64 Santi Raffa
405 6488e5bc Santi Raffa
  def GetUserspaceAccessUri(self, hypervisor):
406 6488e5bc Santi Raffa
    """Generate KVM userspace URIs to be used as `-drive file` settings.
407 6488e5bc Santi Raffa

408 6488e5bc Santi Raffa
    @see: L{BlockDev.GetUserspaceAccessUri}
409 6488e5bc Santi Raffa
    @see: https://github.com/qemu/qemu/commit/8d6d89cb63c57569864ecdeb84d3a1c2eb
410 6488e5bc Santi Raffa
    """
411 6488e5bc Santi Raffa
412 6488e5bc Santi Raffa
    if hypervisor == constants.HT_KVM:
413 6488e5bc Santi Raffa
      return self.volume.GetKVMMountString(self.path)
414 6488e5bc Santi Raffa
    else:
415 6488e5bc Santi Raffa
      base.ThrowError("Hypervisor %s doesn't support Gluster userspace access" %
416 6488e5bc Santi Raffa
                      hypervisor)
417 6488e5bc Santi Raffa
418 8106dd64 Santi Raffa
  @classmethod
419 8106dd64 Santi Raffa
  def Create(cls, unique_id, children, size, spindles, params, excl_stor,
420 bddc92ee Klaus Aehlig
             dyn_params, *args):
421 8106dd64 Santi Raffa
    """Create a new file.
422 8106dd64 Santi Raffa

423 8106dd64 Santi Raffa
    @param size: the size of file in MiB
424 8106dd64 Santi Raffa

425 8106dd64 Santi Raffa
    @rtype: L{bdev.FileStorage}
426 8106dd64 Santi Raffa
    @return: an instance of FileStorage
427 8106dd64 Santi Raffa

428 8106dd64 Santi Raffa
    """
429 8106dd64 Santi Raffa
    if excl_stor:
430 8106dd64 Santi Raffa
      raise errors.ProgrammerError("FileStorage device requested with"
431 8106dd64 Santi Raffa
                                   " exclusive_storage")
432 8106dd64 Santi Raffa
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
433 8106dd64 Santi Raffa
      raise ValueError("Invalid configuration data %s" % str(unique_id))
434 8106dd64 Santi Raffa
435 845b7ed1 Santi Raffa
    full_path = unique_id[1]
436 845b7ed1 Santi Raffa
437 845b7ed1 Santi Raffa
    server_addr = params[constants.GLUSTER_HOST]
438 845b7ed1 Santi Raffa
    port = params[constants.GLUSTER_PORT]
439 845b7ed1 Santi Raffa
    volume = params[constants.GLUSTER_VOLUME]
440 845b7ed1 Santi Raffa
441 845b7ed1 Santi Raffa
    volume_obj = GlusterVolume(server_addr, port, volume)
442 845b7ed1 Santi Raffa
    full_path = io.PathJoin(volume_obj.mount_point, full_path)
443 845b7ed1 Santi Raffa
444 845b7ed1 Santi Raffa
    # Possible optimization: defer actual creation to first Attach, rather
445 845b7ed1 Santi Raffa
    # than mounting and unmounting here, then remounting immediately after.
446 845b7ed1 Santi Raffa
    with volume_obj.Mount():
447 845b7ed1 Santi Raffa
      FileDeviceHelper.CreateFile(full_path, size, create_folders=True)
448 8106dd64 Santi Raffa
449 bddc92ee Klaus Aehlig
    return GlusterStorage(unique_id, children, size, params, dyn_params,
450 bddc92ee Klaus Aehlig
                          *args)