root / lib / storage / extstorage.py @ ab0c6a39
History | View | Annotate | Download (15.3 kB)
1 | 87e9bdb7 | Ilias Tsitsimpis | #
|
---|---|---|---|
2 | 87e9bdb7 | Ilias Tsitsimpis | #
|
3 | 87e9bdb7 | Ilias Tsitsimpis | |
4 | 87e9bdb7 | Ilias Tsitsimpis | # Copyright (C) 2014 Google Inc.
|
5 | 87e9bdb7 | Ilias Tsitsimpis | #
|
6 | 87e9bdb7 | Ilias Tsitsimpis | # This program is free software; you can redistribute it and/or modify
|
7 | 87e9bdb7 | Ilias Tsitsimpis | # it under the terms of the GNU General Public License as published by
|
8 | 87e9bdb7 | Ilias Tsitsimpis | # the Free Software Foundation; either version 2 of the License, or
|
9 | 87e9bdb7 | Ilias Tsitsimpis | # (at your option) any later version.
|
10 | 87e9bdb7 | Ilias Tsitsimpis | #
|
11 | 87e9bdb7 | Ilias Tsitsimpis | # This program is distributed in the hope that it will be useful, but
|
12 | 87e9bdb7 | Ilias Tsitsimpis | # WITHOUT ANY WARRANTY; without even the implied warranty of
|
13 | 87e9bdb7 | Ilias Tsitsimpis | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
14 | 87e9bdb7 | Ilias Tsitsimpis | # General Public License for more details.
|
15 | 87e9bdb7 | Ilias Tsitsimpis | #
|
16 | 87e9bdb7 | Ilias Tsitsimpis | # You should have received a copy of the GNU General Public License
|
17 | 87e9bdb7 | Ilias Tsitsimpis | # along with this program; if not, write to the Free Software
|
18 | 87e9bdb7 | Ilias Tsitsimpis | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
19 | 87e9bdb7 | Ilias Tsitsimpis | # 02110-1301, USA.
|
20 | 87e9bdb7 | Ilias Tsitsimpis | |
21 | 87e9bdb7 | Ilias Tsitsimpis | |
22 | 87e9bdb7 | Ilias Tsitsimpis | """ExtStorage Interface related functionality
|
23 | 87e9bdb7 | Ilias Tsitsimpis |
|
24 | 87e9bdb7 | Ilias Tsitsimpis | """
|
25 | 87e9bdb7 | Ilias Tsitsimpis | |
26 | 87e9bdb7 | Ilias Tsitsimpis | import re |
27 | 87e9bdb7 | Ilias Tsitsimpis | import stat |
28 | 87e9bdb7 | Ilias Tsitsimpis | import os |
29 | 87e9bdb7 | Ilias Tsitsimpis | import logging |
30 | 87e9bdb7 | Ilias Tsitsimpis | |
31 | 87e9bdb7 | Ilias Tsitsimpis | from ganeti import utils |
32 | 87e9bdb7 | Ilias Tsitsimpis | from ganeti import errors |
33 | 87e9bdb7 | Ilias Tsitsimpis | from ganeti import constants |
34 | 87e9bdb7 | Ilias Tsitsimpis | from ganeti import objects |
35 | 87e9bdb7 | Ilias Tsitsimpis | from ganeti import pathutils |
36 | 87e9bdb7 | Ilias Tsitsimpis | from ganeti.storage import base |
37 | 87e9bdb7 | Ilias Tsitsimpis | |
38 | 87e9bdb7 | Ilias Tsitsimpis | |
39 | 87e9bdb7 | Ilias Tsitsimpis | class ExtStorageDevice(base.BlockDev): |
40 | 87e9bdb7 | Ilias Tsitsimpis | """A block device provided by an ExtStorage Provider.
|
41 | 87e9bdb7 | Ilias Tsitsimpis |
|
42 | 87e9bdb7 | Ilias Tsitsimpis | This class implements the External Storage Interface, which means
|
43 | 87e9bdb7 | Ilias Tsitsimpis | handling of the externally provided block devices.
|
44 | 87e9bdb7 | Ilias Tsitsimpis |
|
45 | 87e9bdb7 | Ilias Tsitsimpis | """
|
46 | 87e9bdb7 | Ilias Tsitsimpis | def __init__(self, unique_id, children, size, params, dyn_params, *args): |
47 | 87e9bdb7 | Ilias Tsitsimpis | """Attaches to an extstorage block device.
|
48 | 87e9bdb7 | Ilias Tsitsimpis |
|
49 | 87e9bdb7 | Ilias Tsitsimpis | """
|
50 | 87e9bdb7 | Ilias Tsitsimpis | super(ExtStorageDevice, self).__init__(unique_id, children, size, params, |
51 | 87e9bdb7 | Ilias Tsitsimpis | dyn_params, *args) |
52 | 87e9bdb7 | Ilias Tsitsimpis | (self.name, self.uuid) = args |
53 | 87e9bdb7 | Ilias Tsitsimpis | |
54 | 87e9bdb7 | Ilias Tsitsimpis | if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2: |
55 | 87e9bdb7 | Ilias Tsitsimpis | raise ValueError("Invalid configuration data %s" % str(unique_id)) |
56 | 87e9bdb7 | Ilias Tsitsimpis | |
57 | 87e9bdb7 | Ilias Tsitsimpis | self.driver, self.vol_name = unique_id |
58 | 87e9bdb7 | Ilias Tsitsimpis | self.ext_params = params
|
59 | 87e9bdb7 | Ilias Tsitsimpis | |
60 | 87e9bdb7 | Ilias Tsitsimpis | self.major = self.minor = None |
61 | 13d30fe9 | Ilias Tsitsimpis | self.uris = []
|
62 | 87e9bdb7 | Ilias Tsitsimpis | self.Attach()
|
63 | 87e9bdb7 | Ilias Tsitsimpis | |
64 | 87e9bdb7 | Ilias Tsitsimpis | @classmethod
|
65 | 87e9bdb7 | Ilias Tsitsimpis | def Create(cls, unique_id, children, size, spindles, params, excl_stor, |
66 | 87e9bdb7 | Ilias Tsitsimpis | dyn_params, *args): |
67 | 87e9bdb7 | Ilias Tsitsimpis | """Create a new extstorage device.
|
68 | 87e9bdb7 | Ilias Tsitsimpis |
|
69 | 87e9bdb7 | Ilias Tsitsimpis | Provision a new volume using an extstorage provider, which will
|
70 | 87e9bdb7 | Ilias Tsitsimpis | then be mapped to a block device.
|
71 | 87e9bdb7 | Ilias Tsitsimpis |
|
72 | 87e9bdb7 | Ilias Tsitsimpis | """
|
73 | 87e9bdb7 | Ilias Tsitsimpis | (name, uuid) = args |
74 | 87e9bdb7 | Ilias Tsitsimpis | |
75 | 87e9bdb7 | Ilias Tsitsimpis | if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2: |
76 | 87e9bdb7 | Ilias Tsitsimpis | raise errors.ProgrammerError("Invalid configuration data %s" % |
77 | 87e9bdb7 | Ilias Tsitsimpis | str(unique_id))
|
78 | 87e9bdb7 | Ilias Tsitsimpis | if excl_stor:
|
79 | 87e9bdb7 | Ilias Tsitsimpis | raise errors.ProgrammerError("extstorage device requested with" |
80 | 87e9bdb7 | Ilias Tsitsimpis | " exclusive_storage")
|
81 | 87e9bdb7 | Ilias Tsitsimpis | |
82 | 87e9bdb7 | Ilias Tsitsimpis | # Call the External Storage's create script,
|
83 | 87e9bdb7 | Ilias Tsitsimpis | # to provision a new Volume inside the External Storage
|
84 | 87e9bdb7 | Ilias Tsitsimpis | _ExtStorageAction(constants.ES_ACTION_CREATE, unique_id, |
85 | 87e9bdb7 | Ilias Tsitsimpis | params, size=str(size), name=name, uuid=uuid)
|
86 | 87e9bdb7 | Ilias Tsitsimpis | |
87 | 87e9bdb7 | Ilias Tsitsimpis | return ExtStorageDevice(unique_id, children, size, params, dyn_params,
|
88 | 87e9bdb7 | Ilias Tsitsimpis | *args) |
89 | 87e9bdb7 | Ilias Tsitsimpis | |
90 | 87e9bdb7 | Ilias Tsitsimpis | def Remove(self): |
91 | 87e9bdb7 | Ilias Tsitsimpis | """Remove the extstorage device.
|
92 | 87e9bdb7 | Ilias Tsitsimpis |
|
93 | 87e9bdb7 | Ilias Tsitsimpis | """
|
94 | 87e9bdb7 | Ilias Tsitsimpis | if not self.minor and not self.Attach(): |
95 | 87e9bdb7 | Ilias Tsitsimpis | # The extstorage device doesn't exist.
|
96 | 87e9bdb7 | Ilias Tsitsimpis | return
|
97 | 87e9bdb7 | Ilias Tsitsimpis | |
98 | 87e9bdb7 | Ilias Tsitsimpis | # First shutdown the device (remove mappings).
|
99 | 87e9bdb7 | Ilias Tsitsimpis | self.Shutdown()
|
100 | 87e9bdb7 | Ilias Tsitsimpis | |
101 | 87e9bdb7 | Ilias Tsitsimpis | # Call the External Storage's remove script,
|
102 | 87e9bdb7 | Ilias Tsitsimpis | # to remove the Volume from the External Storage
|
103 | 87e9bdb7 | Ilias Tsitsimpis | _ExtStorageAction(constants.ES_ACTION_REMOVE, self.unique_id,
|
104 | 87e9bdb7 | Ilias Tsitsimpis | self.ext_params, name=self.name, uuid=self.uuid) |
105 | 87e9bdb7 | Ilias Tsitsimpis | |
106 | 87e9bdb7 | Ilias Tsitsimpis | def Rename(self, new_id): |
107 | 87e9bdb7 | Ilias Tsitsimpis | """Rename this device.
|
108 | 87e9bdb7 | Ilias Tsitsimpis |
|
109 | 87e9bdb7 | Ilias Tsitsimpis | """
|
110 | 87e9bdb7 | Ilias Tsitsimpis | pass
|
111 | 87e9bdb7 | Ilias Tsitsimpis | |
112 | 87e9bdb7 | Ilias Tsitsimpis | def Attach(self): |
113 | 87e9bdb7 | Ilias Tsitsimpis | """Attach to an existing extstorage device.
|
114 | 87e9bdb7 | Ilias Tsitsimpis |
|
115 | 87e9bdb7 | Ilias Tsitsimpis | This method maps the extstorage volume that matches our name with
|
116 | 87e9bdb7 | Ilias Tsitsimpis | a corresponding block device and then attaches to this device.
|
117 | 87e9bdb7 | Ilias Tsitsimpis |
|
118 | 87e9bdb7 | Ilias Tsitsimpis | """
|
119 | 87e9bdb7 | Ilias Tsitsimpis | self.attached = False |
120 | 87e9bdb7 | Ilias Tsitsimpis | |
121 | 87e9bdb7 | Ilias Tsitsimpis | # Call the External Storage's attach script,
|
122 | 87e9bdb7 | Ilias Tsitsimpis | # to attach an existing Volume to a block device under /dev
|
123 | 13d30fe9 | Ilias Tsitsimpis | result = _ExtStorageAction(constants.ES_ACTION_ATTACH, |
124 | 13d30fe9 | Ilias Tsitsimpis | self.unique_id, self.ext_params, |
125 | 13d30fe9 | Ilias Tsitsimpis | name=self.name, uuid=self.uuid) |
126 | 13d30fe9 | Ilias Tsitsimpis | |
127 | 13d30fe9 | Ilias Tsitsimpis | # Attach script returns the block device path and optionally
|
128 | 13d30fe9 | Ilias Tsitsimpis | # the URIs to be used for userspace access (one URI for
|
129 | 13d30fe9 | Ilias Tsitsimpis | # each hypervisor supported).
|
130 | 13d30fe9 | Ilias Tsitsimpis | # If the provider doesn't support userspace access, then
|
131 | 13d30fe9 | Ilias Tsitsimpis | # the 'uris' variable will be an empty list.
|
132 | 13d30fe9 | Ilias Tsitsimpis | result = result.split("\n")
|
133 | 13d30fe9 | Ilias Tsitsimpis | self.dev_path = result[0] |
134 | 13d30fe9 | Ilias Tsitsimpis | self.uris = result[1:] |
135 | 13d30fe9 | Ilias Tsitsimpis | |
136 | 13d30fe9 | Ilias Tsitsimpis | # Verify that dev_path exists and is a block device
|
137 | 87e9bdb7 | Ilias Tsitsimpis | try:
|
138 | 87e9bdb7 | Ilias Tsitsimpis | st = os.stat(self.dev_path)
|
139 | 87e9bdb7 | Ilias Tsitsimpis | except OSError, err: |
140 | 87e9bdb7 | Ilias Tsitsimpis | logging.error("Error stat()'ing %s: %s", self.dev_path, str(err)) |
141 | 87e9bdb7 | Ilias Tsitsimpis | return False |
142 | 87e9bdb7 | Ilias Tsitsimpis | |
143 | 87e9bdb7 | Ilias Tsitsimpis | if not stat.S_ISBLK(st.st_mode): |
144 | 87e9bdb7 | Ilias Tsitsimpis | logging.error("%s is not a block device", self.dev_path) |
145 | 87e9bdb7 | Ilias Tsitsimpis | return False |
146 | 87e9bdb7 | Ilias Tsitsimpis | |
147 | 87e9bdb7 | Ilias Tsitsimpis | self.major = os.major(st.st_rdev)
|
148 | 87e9bdb7 | Ilias Tsitsimpis | self.minor = os.minor(st.st_rdev)
|
149 | 87e9bdb7 | Ilias Tsitsimpis | self.attached = True |
150 | 87e9bdb7 | Ilias Tsitsimpis | |
151 | 87e9bdb7 | Ilias Tsitsimpis | return True |
152 | 87e9bdb7 | Ilias Tsitsimpis | |
153 | 87e9bdb7 | Ilias Tsitsimpis | def Assemble(self): |
154 | 87e9bdb7 | Ilias Tsitsimpis | """Assemble the device.
|
155 | 87e9bdb7 | Ilias Tsitsimpis |
|
156 | 87e9bdb7 | Ilias Tsitsimpis | """
|
157 | 87e9bdb7 | Ilias Tsitsimpis | pass
|
158 | 87e9bdb7 | Ilias Tsitsimpis | |
159 | 87e9bdb7 | Ilias Tsitsimpis | def Shutdown(self): |
160 | 87e9bdb7 | Ilias Tsitsimpis | """Shutdown the device.
|
161 | 87e9bdb7 | Ilias Tsitsimpis |
|
162 | 87e9bdb7 | Ilias Tsitsimpis | """
|
163 | 87e9bdb7 | Ilias Tsitsimpis | if not self.minor and not self.Attach(): |
164 | 87e9bdb7 | Ilias Tsitsimpis | # The extstorage device doesn't exist.
|
165 | 87e9bdb7 | Ilias Tsitsimpis | return
|
166 | 87e9bdb7 | Ilias Tsitsimpis | |
167 | 87e9bdb7 | Ilias Tsitsimpis | # Call the External Storage's detach script,
|
168 | 87e9bdb7 | Ilias Tsitsimpis | # to detach an existing Volume from it's block device under /dev
|
169 | 87e9bdb7 | Ilias Tsitsimpis | _ExtStorageAction(constants.ES_ACTION_DETACH, self.unique_id,
|
170 | 87e9bdb7 | Ilias Tsitsimpis | self.ext_params, name=self.name, uuid=self.uuid) |
171 | 87e9bdb7 | Ilias Tsitsimpis | |
172 | 87e9bdb7 | Ilias Tsitsimpis | self.minor = None |
173 | 87e9bdb7 | Ilias Tsitsimpis | self.dev_path = None |
174 | 87e9bdb7 | Ilias Tsitsimpis | |
175 | 87e9bdb7 | Ilias Tsitsimpis | def Open(self, force=False): |
176 | 87e9bdb7 | Ilias Tsitsimpis | """Make the device ready for I/O.
|
177 | 87e9bdb7 | Ilias Tsitsimpis |
|
178 | 87e9bdb7 | Ilias Tsitsimpis | """
|
179 | 87e9bdb7 | Ilias Tsitsimpis | pass
|
180 | 87e9bdb7 | Ilias Tsitsimpis | |
181 | 87e9bdb7 | Ilias Tsitsimpis | def Close(self): |
182 | 87e9bdb7 | Ilias Tsitsimpis | """Notifies that the device will no longer be used for I/O.
|
183 | 87e9bdb7 | Ilias Tsitsimpis |
|
184 | 87e9bdb7 | Ilias Tsitsimpis | """
|
185 | 87e9bdb7 | Ilias Tsitsimpis | pass
|
186 | 87e9bdb7 | Ilias Tsitsimpis | |
187 | 87e9bdb7 | Ilias Tsitsimpis | def Grow(self, amount, dryrun, backingstore, excl_stor): |
188 | 87e9bdb7 | Ilias Tsitsimpis | """Grow the Volume.
|
189 | 87e9bdb7 | Ilias Tsitsimpis |
|
190 | 87e9bdb7 | Ilias Tsitsimpis | @type amount: integer
|
191 | 87e9bdb7 | Ilias Tsitsimpis | @param amount: the amount (in mebibytes) to grow with
|
192 | 87e9bdb7 | Ilias Tsitsimpis | @type dryrun: boolean
|
193 | 87e9bdb7 | Ilias Tsitsimpis | @param dryrun: whether to execute the operation in simulation mode
|
194 | 87e9bdb7 | Ilias Tsitsimpis | only, without actually increasing the size
|
195 | 87e9bdb7 | Ilias Tsitsimpis |
|
196 | 87e9bdb7 | Ilias Tsitsimpis | """
|
197 | 87e9bdb7 | Ilias Tsitsimpis | if not backingstore: |
198 | 87e9bdb7 | Ilias Tsitsimpis | return
|
199 | 87e9bdb7 | Ilias Tsitsimpis | if not self.Attach(): |
200 | 87e9bdb7 | Ilias Tsitsimpis | base.ThrowError("Can't attach to extstorage device during Grow()")
|
201 | 87e9bdb7 | Ilias Tsitsimpis | |
202 | 87e9bdb7 | Ilias Tsitsimpis | if dryrun:
|
203 | 87e9bdb7 | Ilias Tsitsimpis | # we do not support dry runs of resize operations for now.
|
204 | 87e9bdb7 | Ilias Tsitsimpis | return
|
205 | 87e9bdb7 | Ilias Tsitsimpis | |
206 | 87e9bdb7 | Ilias Tsitsimpis | new_size = self.size + amount
|
207 | 87e9bdb7 | Ilias Tsitsimpis | |
208 | 87e9bdb7 | Ilias Tsitsimpis | # Call the External Storage's grow script,
|
209 | 87e9bdb7 | Ilias Tsitsimpis | # to grow an existing Volume inside the External Storage
|
210 | 87e9bdb7 | Ilias Tsitsimpis | _ExtStorageAction(constants.ES_ACTION_GROW, self.unique_id,
|
211 | 87e9bdb7 | Ilias Tsitsimpis | self.ext_params, size=str(self.size), grow=str(new_size), |
212 | 87e9bdb7 | Ilias Tsitsimpis | name=self.name, uuid=self.uuid) |
213 | 87e9bdb7 | Ilias Tsitsimpis | |
214 | 87e9bdb7 | Ilias Tsitsimpis | def SetInfo(self, text): |
215 | 87e9bdb7 | Ilias Tsitsimpis | """Update metadata with info text.
|
216 | 87e9bdb7 | Ilias Tsitsimpis |
|
217 | 87e9bdb7 | Ilias Tsitsimpis | """
|
218 | 87e9bdb7 | Ilias Tsitsimpis | # Replace invalid characters
|
219 | 87e9bdb7 | Ilias Tsitsimpis | text = re.sub("^[^A-Za-z0-9_+.]", "_", text) |
220 | 87e9bdb7 | Ilias Tsitsimpis | text = re.sub("[^-A-Za-z0-9_+.]", "_", text) |
221 | 87e9bdb7 | Ilias Tsitsimpis | |
222 | 87e9bdb7 | Ilias Tsitsimpis | # Only up to 128 characters are allowed
|
223 | 87e9bdb7 | Ilias Tsitsimpis | text = text[:128]
|
224 | 87e9bdb7 | Ilias Tsitsimpis | |
225 | 87e9bdb7 | Ilias Tsitsimpis | # Call the External Storage's setinfo script,
|
226 | 87e9bdb7 | Ilias Tsitsimpis | # to set metadata for an existing Volume inside the External Storage
|
227 | 87e9bdb7 | Ilias Tsitsimpis | _ExtStorageAction(constants.ES_ACTION_SETINFO, self.unique_id,
|
228 | 87e9bdb7 | Ilias Tsitsimpis | self.ext_params, metadata=text,
|
229 | 87e9bdb7 | Ilias Tsitsimpis | name=self.name, uuid=self.uuid) |
230 | 87e9bdb7 | Ilias Tsitsimpis | |
231 | 13d30fe9 | Ilias Tsitsimpis | def GetUserspaceAccessUri(self, hypervisor): |
232 | 13d30fe9 | Ilias Tsitsimpis | """Generate KVM userspace URIs to be used as `-drive file` settings.
|
233 | 13d30fe9 | Ilias Tsitsimpis |
|
234 | 13d30fe9 | Ilias Tsitsimpis | @see: L{base.BlockDev.GetUserspaceAccessUri}
|
235 | 13d30fe9 | Ilias Tsitsimpis |
|
236 | 13d30fe9 | Ilias Tsitsimpis | """
|
237 | 13d30fe9 | Ilias Tsitsimpis | if not self.Attach(): |
238 | 13d30fe9 | Ilias Tsitsimpis | base.ThrowError("Can't attach to ExtStorage device")
|
239 | 13d30fe9 | Ilias Tsitsimpis | |
240 | 13d30fe9 | Ilias Tsitsimpis | # If the provider supports userspace access, the attach script has
|
241 | 13d30fe9 | Ilias Tsitsimpis | # returned a list of URIs prefixed with the corresponding hypervisor.
|
242 | 13d30fe9 | Ilias Tsitsimpis | prefix = hypervisor.lower() + ":"
|
243 | 13d30fe9 | Ilias Tsitsimpis | for uri in self.uris: |
244 | 13d30fe9 | Ilias Tsitsimpis | if uri[:len(prefix)].lower() == prefix: |
245 | 13d30fe9 | Ilias Tsitsimpis | return uri[len(prefix):] |
246 | 13d30fe9 | Ilias Tsitsimpis | |
247 | 13d30fe9 | Ilias Tsitsimpis | base.ThrowError("Userspace access is not supported by the '%s'"
|
248 | 13d30fe9 | Ilias Tsitsimpis | " ExtStorage provider for the '%s' hypervisor"
|
249 | 13d30fe9 | Ilias Tsitsimpis | % (self.driver, hypervisor))
|
250 | 13d30fe9 | Ilias Tsitsimpis | |
251 | 87e9bdb7 | Ilias Tsitsimpis | |
252 | 87e9bdb7 | Ilias Tsitsimpis | def _ExtStorageAction(action, unique_id, ext_params, |
253 | 87e9bdb7 | Ilias Tsitsimpis | size=None, grow=None, metadata=None, |
254 | 87e9bdb7 | Ilias Tsitsimpis | name=None, uuid=None): |
255 | 87e9bdb7 | Ilias Tsitsimpis | """Take an External Storage action.
|
256 | 87e9bdb7 | Ilias Tsitsimpis |
|
257 | 87e9bdb7 | Ilias Tsitsimpis | Take an External Storage action concerning or affecting
|
258 | 87e9bdb7 | Ilias Tsitsimpis | a specific Volume inside the External Storage.
|
259 | 87e9bdb7 | Ilias Tsitsimpis |
|
260 | 87e9bdb7 | Ilias Tsitsimpis | @type action: string
|
261 | 87e9bdb7 | Ilias Tsitsimpis | @param action: which action to perform. One of:
|
262 | 87e9bdb7 | Ilias Tsitsimpis | create / remove / grow / attach / detach
|
263 | 87e9bdb7 | Ilias Tsitsimpis | @type unique_id: tuple (driver, vol_name)
|
264 | 87e9bdb7 | Ilias Tsitsimpis | @param unique_id: a tuple containing the type of ExtStorage (driver)
|
265 | 87e9bdb7 | Ilias Tsitsimpis | and the Volume name
|
266 | 87e9bdb7 | Ilias Tsitsimpis | @type ext_params: dict
|
267 | 87e9bdb7 | Ilias Tsitsimpis | @param ext_params: ExtStorage parameters
|
268 | 87e9bdb7 | Ilias Tsitsimpis | @type size: integer
|
269 | 87e9bdb7 | Ilias Tsitsimpis | @param size: the size of the Volume in mebibytes
|
270 | 87e9bdb7 | Ilias Tsitsimpis | @type grow: integer
|
271 | 87e9bdb7 | Ilias Tsitsimpis | @param grow: the new size in mebibytes (after grow)
|
272 | 87e9bdb7 | Ilias Tsitsimpis | @type metadata: string
|
273 | 87e9bdb7 | Ilias Tsitsimpis | @param metadata: metadata info of the Volume, for use by the provider
|
274 | 87e9bdb7 | Ilias Tsitsimpis | @type name: string
|
275 | 87e9bdb7 | Ilias Tsitsimpis | @param name: name of the Volume (objects.Disk.name)
|
276 | 87e9bdb7 | Ilias Tsitsimpis | @type uuid: string
|
277 | 87e9bdb7 | Ilias Tsitsimpis | @param uuid: uuid of the Volume (objects.Disk.uuid)
|
278 | 87e9bdb7 | Ilias Tsitsimpis | @rtype: None or a block device path (during attach)
|
279 | 87e9bdb7 | Ilias Tsitsimpis |
|
280 | 87e9bdb7 | Ilias Tsitsimpis | """
|
281 | 87e9bdb7 | Ilias Tsitsimpis | driver, vol_name = unique_id |
282 | 87e9bdb7 | Ilias Tsitsimpis | |
283 | 87e9bdb7 | Ilias Tsitsimpis | # Create an External Storage instance of type `driver'
|
284 | 87e9bdb7 | Ilias Tsitsimpis | status, inst_es = ExtStorageFromDisk(driver) |
285 | 87e9bdb7 | Ilias Tsitsimpis | if not status: |
286 | 87e9bdb7 | Ilias Tsitsimpis | base.ThrowError("%s" % inst_es)
|
287 | 87e9bdb7 | Ilias Tsitsimpis | |
288 | 87e9bdb7 | Ilias Tsitsimpis | # Create the basic environment for the driver's scripts
|
289 | 87e9bdb7 | Ilias Tsitsimpis | create_env = _ExtStorageEnvironment(unique_id, ext_params, size, |
290 | 87e9bdb7 | Ilias Tsitsimpis | grow, metadata, name, uuid) |
291 | 87e9bdb7 | Ilias Tsitsimpis | |
292 | 87e9bdb7 | Ilias Tsitsimpis | # Do not use log file for action `attach' as we need
|
293 | 87e9bdb7 | Ilias Tsitsimpis | # to get the output from RunResult
|
294 | 87e9bdb7 | Ilias Tsitsimpis | # TODO: find a way to have a log file for attach too
|
295 | 87e9bdb7 | Ilias Tsitsimpis | logfile = None
|
296 | 87e9bdb7 | Ilias Tsitsimpis | if action is not constants.ES_ACTION_ATTACH: |
297 | 87e9bdb7 | Ilias Tsitsimpis | logfile = _VolumeLogName(action, driver, vol_name) |
298 | 87e9bdb7 | Ilias Tsitsimpis | |
299 | 87e9bdb7 | Ilias Tsitsimpis | # Make sure the given action results in a valid script
|
300 | 87e9bdb7 | Ilias Tsitsimpis | if action not in constants.ES_SCRIPTS: |
301 | 87e9bdb7 | Ilias Tsitsimpis | base.ThrowError("Action '%s' doesn't result in a valid ExtStorage script" %
|
302 | 87e9bdb7 | Ilias Tsitsimpis | action) |
303 | 87e9bdb7 | Ilias Tsitsimpis | |
304 | 87e9bdb7 | Ilias Tsitsimpis | # Find out which external script to run according the given action
|
305 | 87e9bdb7 | Ilias Tsitsimpis | script_name = action + "_script"
|
306 | 87e9bdb7 | Ilias Tsitsimpis | script = getattr(inst_es, script_name)
|
307 | 87e9bdb7 | Ilias Tsitsimpis | |
308 | 87e9bdb7 | Ilias Tsitsimpis | # Run the external script
|
309 | 87e9bdb7 | Ilias Tsitsimpis | result = utils.RunCmd([script], env=create_env, |
310 | 87e9bdb7 | Ilias Tsitsimpis | cwd=inst_es.path, output=logfile,) |
311 | 87e9bdb7 | Ilias Tsitsimpis | if result.failed:
|
312 | 87e9bdb7 | Ilias Tsitsimpis | logging.error("External storage's %s command '%s' returned"
|
313 | 87e9bdb7 | Ilias Tsitsimpis | " error: %s, logfile: %s, output: %s",
|
314 | 87e9bdb7 | Ilias Tsitsimpis | action, result.cmd, result.fail_reason, |
315 | 87e9bdb7 | Ilias Tsitsimpis | logfile, result.output) |
316 | 87e9bdb7 | Ilias Tsitsimpis | |
317 | 87e9bdb7 | Ilias Tsitsimpis | # If logfile is 'None' (during attach), it breaks TailFile
|
318 | 87e9bdb7 | Ilias Tsitsimpis | # TODO: have a log file for attach too
|
319 | 87e9bdb7 | Ilias Tsitsimpis | if action is not constants.ES_ACTION_ATTACH: |
320 | 87e9bdb7 | Ilias Tsitsimpis | lines = [utils.SafeEncode(val) |
321 | 87e9bdb7 | Ilias Tsitsimpis | for val in utils.TailFile(logfile, lines=20)] |
322 | 87e9bdb7 | Ilias Tsitsimpis | else:
|
323 | 87e9bdb7 | Ilias Tsitsimpis | lines = result.output[-20:]
|
324 | 87e9bdb7 | Ilias Tsitsimpis | |
325 | 87e9bdb7 | Ilias Tsitsimpis | base.ThrowError("External storage's %s script failed (%s), last"
|
326 | 87e9bdb7 | Ilias Tsitsimpis | " lines of output:\n%s",
|
327 | 87e9bdb7 | Ilias Tsitsimpis | action, result.fail_reason, "\n".join(lines))
|
328 | 87e9bdb7 | Ilias Tsitsimpis | |
329 | 87e9bdb7 | Ilias Tsitsimpis | if action == constants.ES_ACTION_ATTACH:
|
330 | 87e9bdb7 | Ilias Tsitsimpis | return result.stdout
|
331 | 87e9bdb7 | Ilias Tsitsimpis | |
332 | 87e9bdb7 | Ilias Tsitsimpis | |
333 | 87e9bdb7 | Ilias Tsitsimpis | def ExtStorageFromDisk(name, base_dir=None): |
334 | 87e9bdb7 | Ilias Tsitsimpis | """Create an ExtStorage instance from disk.
|
335 | 87e9bdb7 | Ilias Tsitsimpis |
|
336 | 87e9bdb7 | Ilias Tsitsimpis | This function will return an ExtStorage instance
|
337 | 87e9bdb7 | Ilias Tsitsimpis | if the given name is a valid ExtStorage name.
|
338 | 87e9bdb7 | Ilias Tsitsimpis |
|
339 | 87e9bdb7 | Ilias Tsitsimpis | @type base_dir: string
|
340 | 87e9bdb7 | Ilias Tsitsimpis | @keyword base_dir: Base directory containing ExtStorage installations.
|
341 | 87e9bdb7 | Ilias Tsitsimpis | Defaults to a search in all the ES_SEARCH_PATH dirs.
|
342 | 87e9bdb7 | Ilias Tsitsimpis | @rtype: tuple
|
343 | 87e9bdb7 | Ilias Tsitsimpis | @return: True and the ExtStorage instance if we find a valid one, or
|
344 | 87e9bdb7 | Ilias Tsitsimpis | False and the diagnose message on error
|
345 | 87e9bdb7 | Ilias Tsitsimpis |
|
346 | 87e9bdb7 | Ilias Tsitsimpis | """
|
347 | 87e9bdb7 | Ilias Tsitsimpis | if base_dir is None: |
348 | 87e9bdb7 | Ilias Tsitsimpis | es_base_dir = pathutils.ES_SEARCH_PATH |
349 | 87e9bdb7 | Ilias Tsitsimpis | else:
|
350 | 87e9bdb7 | Ilias Tsitsimpis | es_base_dir = [base_dir] |
351 | 87e9bdb7 | Ilias Tsitsimpis | |
352 | 87e9bdb7 | Ilias Tsitsimpis | es_dir = utils.FindFile(name, es_base_dir, os.path.isdir) |
353 | 87e9bdb7 | Ilias Tsitsimpis | |
354 | 87e9bdb7 | Ilias Tsitsimpis | if es_dir is None: |
355 | 87e9bdb7 | Ilias Tsitsimpis | return False, ("Directory for External Storage Provider %s not" |
356 | 87e9bdb7 | Ilias Tsitsimpis | " found in search path" % name)
|
357 | 87e9bdb7 | Ilias Tsitsimpis | |
358 | 87e9bdb7 | Ilias Tsitsimpis | # ES Files dictionary, we will populate it with the absolute path
|
359 | 87e9bdb7 | Ilias Tsitsimpis | # names; if the value is True, then it is a required file, otherwise
|
360 | 87e9bdb7 | Ilias Tsitsimpis | # an optional one
|
361 | 87e9bdb7 | Ilias Tsitsimpis | es_files = dict.fromkeys(constants.ES_SCRIPTS, True) |
362 | 87e9bdb7 | Ilias Tsitsimpis | |
363 | 87e9bdb7 | Ilias Tsitsimpis | es_files[constants.ES_PARAMETERS_FILE] = True
|
364 | 87e9bdb7 | Ilias Tsitsimpis | |
365 | 87e9bdb7 | Ilias Tsitsimpis | for (filename, _) in es_files.items(): |
366 | 87e9bdb7 | Ilias Tsitsimpis | es_files[filename] = utils.PathJoin(es_dir, filename) |
367 | 87e9bdb7 | Ilias Tsitsimpis | |
368 | 87e9bdb7 | Ilias Tsitsimpis | try:
|
369 | 87e9bdb7 | Ilias Tsitsimpis | st = os.stat(es_files[filename]) |
370 | 87e9bdb7 | Ilias Tsitsimpis | except EnvironmentError, err: |
371 | 87e9bdb7 | Ilias Tsitsimpis | return False, ("File '%s' under path '%s' is missing (%s)" % |
372 | 87e9bdb7 | Ilias Tsitsimpis | (filename, es_dir, utils.ErrnoOrStr(err))) |
373 | 87e9bdb7 | Ilias Tsitsimpis | |
374 | 87e9bdb7 | Ilias Tsitsimpis | if not stat.S_ISREG(stat.S_IFMT(st.st_mode)): |
375 | 87e9bdb7 | Ilias Tsitsimpis | return False, ("File '%s' under path '%s' is not a regular file" % |
376 | 87e9bdb7 | Ilias Tsitsimpis | (filename, es_dir)) |
377 | 87e9bdb7 | Ilias Tsitsimpis | |
378 | 87e9bdb7 | Ilias Tsitsimpis | if filename in constants.ES_SCRIPTS: |
379 | 87e9bdb7 | Ilias Tsitsimpis | if stat.S_IMODE(st.st_mode) & stat.S_IXUSR != stat.S_IXUSR:
|
380 | 87e9bdb7 | Ilias Tsitsimpis | return False, ("File '%s' under path '%s' is not executable" % |
381 | 87e9bdb7 | Ilias Tsitsimpis | (filename, es_dir)) |
382 | 87e9bdb7 | Ilias Tsitsimpis | |
383 | 87e9bdb7 | Ilias Tsitsimpis | parameters = [] |
384 | 87e9bdb7 | Ilias Tsitsimpis | if constants.ES_PARAMETERS_FILE in es_files: |
385 | 87e9bdb7 | Ilias Tsitsimpis | parameters_file = es_files[constants.ES_PARAMETERS_FILE] |
386 | 87e9bdb7 | Ilias Tsitsimpis | try:
|
387 | 87e9bdb7 | Ilias Tsitsimpis | parameters = utils.ReadFile(parameters_file).splitlines() |
388 | 87e9bdb7 | Ilias Tsitsimpis | except EnvironmentError, err: |
389 | 87e9bdb7 | Ilias Tsitsimpis | return False, ("Error while reading the EXT parameters file at %s: %s" % |
390 | 87e9bdb7 | Ilias Tsitsimpis | (parameters_file, utils.ErrnoOrStr(err))) |
391 | 87e9bdb7 | Ilias Tsitsimpis | parameters = [v.split(None, 1) for v in parameters] |
392 | 87e9bdb7 | Ilias Tsitsimpis | |
393 | 87e9bdb7 | Ilias Tsitsimpis | es_obj = \ |
394 | 87e9bdb7 | Ilias Tsitsimpis | objects.ExtStorage(name=name, path=es_dir, |
395 | 87e9bdb7 | Ilias Tsitsimpis | create_script=es_files[constants.ES_SCRIPT_CREATE], |
396 | 87e9bdb7 | Ilias Tsitsimpis | remove_script=es_files[constants.ES_SCRIPT_REMOVE], |
397 | 87e9bdb7 | Ilias Tsitsimpis | grow_script=es_files[constants.ES_SCRIPT_GROW], |
398 | 87e9bdb7 | Ilias Tsitsimpis | attach_script=es_files[constants.ES_SCRIPT_ATTACH], |
399 | 87e9bdb7 | Ilias Tsitsimpis | detach_script=es_files[constants.ES_SCRIPT_DETACH], |
400 | 87e9bdb7 | Ilias Tsitsimpis | setinfo_script=es_files[constants.ES_SCRIPT_SETINFO], |
401 | 87e9bdb7 | Ilias Tsitsimpis | verify_script=es_files[constants.ES_SCRIPT_VERIFY], |
402 | 87e9bdb7 | Ilias Tsitsimpis | supported_parameters=parameters) |
403 | 87e9bdb7 | Ilias Tsitsimpis | return True, es_obj |
404 | 87e9bdb7 | Ilias Tsitsimpis | |
405 | 87e9bdb7 | Ilias Tsitsimpis | |
406 | 87e9bdb7 | Ilias Tsitsimpis | def _ExtStorageEnvironment(unique_id, ext_params, |
407 | 87e9bdb7 | Ilias Tsitsimpis | size=None, grow=None, metadata=None, |
408 | 87e9bdb7 | Ilias Tsitsimpis | name=None, uuid=None): |
409 | 87e9bdb7 | Ilias Tsitsimpis | """Calculate the environment for an External Storage script.
|
410 | 87e9bdb7 | Ilias Tsitsimpis |
|
411 | 87e9bdb7 | Ilias Tsitsimpis | @type unique_id: tuple (driver, vol_name)
|
412 | 87e9bdb7 | Ilias Tsitsimpis | @param unique_id: ExtStorage pool and name of the Volume
|
413 | 87e9bdb7 | Ilias Tsitsimpis | @type ext_params: dict
|
414 | 87e9bdb7 | Ilias Tsitsimpis | @param ext_params: the EXT parameters
|
415 | 87e9bdb7 | Ilias Tsitsimpis | @type size: string
|
416 | 87e9bdb7 | Ilias Tsitsimpis | @param size: size of the Volume (in mebibytes)
|
417 | 87e9bdb7 | Ilias Tsitsimpis | @type grow: string
|
418 | 87e9bdb7 | Ilias Tsitsimpis | @param grow: new size of Volume after grow (in mebibytes)
|
419 | 87e9bdb7 | Ilias Tsitsimpis | @type metadata: string
|
420 | 87e9bdb7 | Ilias Tsitsimpis | @param metadata: metadata info of the Volume
|
421 | 87e9bdb7 | Ilias Tsitsimpis | @type name: string
|
422 | 87e9bdb7 | Ilias Tsitsimpis | @param name: name of the Volume (objects.Disk.name)
|
423 | 87e9bdb7 | Ilias Tsitsimpis | @type uuid: string
|
424 | 87e9bdb7 | Ilias Tsitsimpis | @param uuid: uuid of the Volume (objects.Disk.uuid)
|
425 | 87e9bdb7 | Ilias Tsitsimpis | @rtype: dict
|
426 | 87e9bdb7 | Ilias Tsitsimpis | @return: dict of environment variables
|
427 | 87e9bdb7 | Ilias Tsitsimpis |
|
428 | 87e9bdb7 | Ilias Tsitsimpis | """
|
429 | 87e9bdb7 | Ilias Tsitsimpis | vol_name = unique_id[1]
|
430 | 87e9bdb7 | Ilias Tsitsimpis | |
431 | 87e9bdb7 | Ilias Tsitsimpis | result = {} |
432 | 87e9bdb7 | Ilias Tsitsimpis | result["VOL_NAME"] = vol_name
|
433 | 87e9bdb7 | Ilias Tsitsimpis | |
434 | 87e9bdb7 | Ilias Tsitsimpis | # EXT params
|
435 | 87e9bdb7 | Ilias Tsitsimpis | for pname, pvalue in ext_params.items(): |
436 | 87e9bdb7 | Ilias Tsitsimpis | result["EXTP_%s" % pname.upper()] = str(pvalue) |
437 | 87e9bdb7 | Ilias Tsitsimpis | |
438 | 87e9bdb7 | Ilias Tsitsimpis | if size is not None: |
439 | 87e9bdb7 | Ilias Tsitsimpis | result["VOL_SIZE"] = size
|
440 | 87e9bdb7 | Ilias Tsitsimpis | |
441 | 87e9bdb7 | Ilias Tsitsimpis | if grow is not None: |
442 | 87e9bdb7 | Ilias Tsitsimpis | result["VOL_NEW_SIZE"] = grow
|
443 | 87e9bdb7 | Ilias Tsitsimpis | |
444 | 87e9bdb7 | Ilias Tsitsimpis | if metadata is not None: |
445 | 87e9bdb7 | Ilias Tsitsimpis | result["VOL_METADATA"] = metadata
|
446 | 87e9bdb7 | Ilias Tsitsimpis | |
447 | 87e9bdb7 | Ilias Tsitsimpis | if name is not None: |
448 | 87e9bdb7 | Ilias Tsitsimpis | result["VOL_CNAME"] = name
|
449 | 87e9bdb7 | Ilias Tsitsimpis | |
450 | 87e9bdb7 | Ilias Tsitsimpis | if uuid is not None: |
451 | 87e9bdb7 | Ilias Tsitsimpis | result["VOL_UUID"] = uuid
|
452 | 87e9bdb7 | Ilias Tsitsimpis | |
453 | 87e9bdb7 | Ilias Tsitsimpis | return result
|
454 | 87e9bdb7 | Ilias Tsitsimpis | |
455 | 87e9bdb7 | Ilias Tsitsimpis | |
456 | 87e9bdb7 | Ilias Tsitsimpis | def _VolumeLogName(kind, es_name, volume): |
457 | 87e9bdb7 | Ilias Tsitsimpis | """Compute the ExtStorage log filename for a given Volume and operation.
|
458 | 87e9bdb7 | Ilias Tsitsimpis |
|
459 | 87e9bdb7 | Ilias Tsitsimpis | @type kind: string
|
460 | 87e9bdb7 | Ilias Tsitsimpis | @param kind: the operation type (e.g. create, remove etc.)
|
461 | 87e9bdb7 | Ilias Tsitsimpis | @type es_name: string
|
462 | 87e9bdb7 | Ilias Tsitsimpis | @param es_name: the ExtStorage name
|
463 | 87e9bdb7 | Ilias Tsitsimpis | @type volume: string
|
464 | 87e9bdb7 | Ilias Tsitsimpis | @param volume: the name of the Volume inside the External Storage
|
465 | 87e9bdb7 | Ilias Tsitsimpis |
|
466 | 87e9bdb7 | Ilias Tsitsimpis | """
|
467 | 87e9bdb7 | Ilias Tsitsimpis | # Check if the extstorage log dir is a valid dir
|
468 | 87e9bdb7 | Ilias Tsitsimpis | if not os.path.isdir(pathutils.LOG_ES_DIR): |
469 | 87e9bdb7 | Ilias Tsitsimpis | base.ThrowError("Cannot find log directory: %s", pathutils.LOG_ES_DIR)
|
470 | 87e9bdb7 | Ilias Tsitsimpis | |
471 | 87e9bdb7 | Ilias Tsitsimpis | # TODO: Use tempfile.mkstemp to create unique filename
|
472 | 87e9bdb7 | Ilias Tsitsimpis | basename = ("%s-%s-%s-%s.log" %
|
473 | 87e9bdb7 | Ilias Tsitsimpis | (kind, es_name, volume, utils.TimestampForFilename())) |
474 | 87e9bdb7 | Ilias Tsitsimpis | return utils.PathJoin(pathutils.LOG_ES_DIR, basename) |