extstorage vlmc scripts refactoring
[archipelago] / xseg / tools / ext_scripts / vlmc_wrapper.py
1 #!/usr/bin/env python
2 #
3 # Copyright (C) 2011 Greek Research and Technology Network
4 #
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 2 of the License, or
8 # (at your option) any later version.
9 #
10 # This program is distributed in the hope that it will be useful, but
11 # WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 # General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18 # 02110-1301, USA.
19
20 """ vlmc provider wrapper-script for ganeti extstorage disk template
21
22 The script takes it's input from environment variables. Specifically the
23 following variables should be present:
24
25  - VOL_NAME: The name of the new Image file
26  - VOL_SIZE: The size of the new Image (in megabytes)
27
28 The following variables are optional:
29
30  - EXTP_ORIGIN: The name of the Image file to snapshot
31
32 The code branches to the correct function, depending on the name (sys.argv[0])
33 of the executed script (attach, create, etc).
34
35 Returns O after successfull completion, 1 on failure
36
37 """
38
39 import os
40 import sys
41
42 from ganeti import utils
43
44
45 def ReadEnv():
46   """Read the enviromental variables
47   """
48
49   name = os.getenv("VOL_NAME")
50   if name is None:
51     sys.stderr.write('The environment variable VOL_NAME is missing.\n')
52     return None
53
54   size = os.getenv("VOL_SIZE")
55   if size is None:
56     sys.stderr.write('The environment variable VOL_SIZE is missing.\n')
57     return None
58
59   origin = os.getenv("EXTP_ORIGIN")
60
61   return (name, size, origin)
62
63 def create(env):
64   """Create a new vlmc Image
65   """
66   sys.stderr.write('Creation started...\n')
67
68   name, size, origin = env
69
70   cmd = ["vlmc", "create", "%s" % name, "--size", "%s" % size]
71   if origin:
72      cmd.extend(["--snap", origin])
73
74   sys.stderr.write('Before RunCmd')
75   result = utils.RunCmd(cmd)
76   sys.stderr.write('After RunCmd')
77
78   if result.failed:
79     sys.stderr.write('vlmc creation failed (%s): %s\n' %
80                      (result.fail_reason, result.output))
81     return 1 
82
83   return 0
84
85 def _ParseVlmcShowmappedOutput(output, volume_name):
86   """Parse the output of `vlmc showmapped'.
87
88   This method parses the output of `vlmc showmapped' and returns
89   the vlmc block device path (e.g. /dev/xsegbd0) that matches the
90   given vlmc volume.
91
92   """
93   allfields = 5
94   volumefield = 2
95   devicefield = 4
96
97   field_sep = "\t"
98
99   lines = output.splitlines()
100   splitted_lines = map(lambda l: l.split(field_sep), lines)
101
102   # Check empty output.
103   if not splitted_lines:
104     sys.stderr.write("vlmc showmapped returned empty output")
105     sys.exit(1)
106
107   # Check showmapped header line, to determine number of fields.
108   field_cnt = len(splitted_lines[0])
109   if field_cnt != allfields:
110     sys.stderr.write("Cannot parse vlmc showmapped output because its format"
111                 " seems to have changed; expected %s fields, found %s",
112                 allfields, field_cnt)
113     sys.exit(1)
114
115   matched_lines = \
116     filter(lambda l: len(l) == allfields and l[volumefield] == volume_name,
117            splitted_lines)
118
119   if len(matched_lines) > 1:
120     sys.stderr.write("The vlmc volume %s is mapped more than once."
121                 " This shouldn't happen, try to unmap the extra"
122                 " devices manually.", volume_name)
123     sys.exit(1) 
124
125   if matched_lines:
126     # vlmc block device found. Return it.
127     dev = matched_lines[0][devicefield]
128     return dev
129
130   # The given volume is not mapped.
131   return None
132
133 def attach(env):
134   """Map an existing vlmc Image to a block device
135
136   This function maps an existing vlmc Image to a block device
137   e.g. /dev/xsegbd{X} and returns the device path. If the mapping
138   already exists, it returns the corresponding device path.
139   """
140
141   name, _, _ = env
142
143   # Check if the mapping already exists
144   cmd = ["vlmc", "showmapped"]
145   result = utils.RunCmd(cmd)
146   if result.failed:
147     sys.stderr.write("vlmc showmapped failed (%s): %s" %
148                      (result.fail_reason, result.output))
149     return 1
150
151   dev = _ParseVlmcShowmappedOutput(result.output, name)
152   if dev:
153     # The mapping exists. Return it.
154     return dev
155
156   # The mapping doesn't exist. Create it.
157   map_cmd = ["vlmc", "map", name]
158   result = utils.RunCmd(map_cmd)
159   if result.failed:
160     sys.stderr.write("vlmc map failed (%s): %s",
161                 result.fail_reason, result.output)
162     return 1
163
164   # Find the corresponding vlmc device.
165   showmap_cmd = ["vlmc", "showmapped"]
166   result = utils.RunCmd(showmap_cmd)
167   if result.failed:
168     sys.stderr.write("vlmc map succeeded, but showmapped failed (%s): %s",
169                 result.fail_reason, result.output)
170     return 1
171
172   dev = self._ParseRbdShowmappedOutput(result.output, name)
173
174   if not dev:
175     sys.stderr.write("vlmc map succeeded, but could not find the vlmc block"
176                 " device in output of showmapped, for volume: %s", name)
177
178   # The device was successfully mapped. Return it.
179   return dev
180
181 def detach(env):
182   """Unmap a vlmc device from the Image it is mapped to
183
184   This function unmaps an vlmc device from the Image it is mapped to.
185   It is idempotent if the mapping doesn't exist at all.
186   """
187   name, _, _ = env
188
189   # Check if the mapping already exists
190   cmd = ["vlmc", "showmapped"]
191   result = utils.RunCmd(cmd)
192   if result.failed:
193     sys.stderr.write("vlmc showmapped failed (%s): %s" %
194                      (result.fail_reason, result.output))
195     return 1
196
197   dev = _ParseVlmcShowmappedOutput(result.output, name)
198
199   if dev:
200     # The mapping exists. Unmap the vlmc device.
201     unmap_cmd = ["vlmc", "unmap", "%s" % dev]
202     result = utils.RunCmd(unmap_cmd)
203     if result.failed:
204       sys.stderr.write("vlmc unmap failed (%s): %s",
205                   result.fail_reason, result.output)
206
207   return 0
208
209 def grow(env):
210   """Grow an existing vlmc Image
211   """
212   name, size, _ = env
213
214   cmd = ["vlmc", "resize", "%s" % name, "--size", "%s" % size]
215
216   result = utils.RunCmd(cmd)
217   if result.failed:
218     sys.stderr.write('vlmc resize failed (%s): %s\n' %
219                      (result.fail_reason, result.output))
220     return 1
221
222   return 0
223
224 def remove(env):
225   """ Delete a vlmc Image
226   """
227
228   name, _, _ = env
229
230   cmd = ["vlmc", "rm", "%s" % name]
231
232   result = utils.RunCmd(cmd)
233   if result.failed:
234     sys.stderr.write("Can't remove Image %s from cluster with vlmc rm: "
235                      "%s - %s" % 
236                      (name, result.fail_reason, result.output))
237     return 1
238
239   return 0
240
241 def verify(env):
242   return 0
243
244 def main():
245   env = ReadEnv()
246   if env is None:
247     sys.stderr.write("Wrong environment. Aborting...\n")
248     return 1
249
250   try:
251     return {
252       'attach': attach(env),
253       'create': create(env),
254       'detach': detach(env),
255       'grow'  : grow(env),
256       'remove': remove(env),
257       'verify': verify(env),
258     }[os.path.basename(sys.argv[0])]
259   except:
260     sys.stderr.write("Op not supported")
261     return 1
262
263 if __name__ == "__main__":
264     sys.exit(main())