Statistics
| Branch: | Revision:

root / shared-filer / grow @ master

History | View | Annotate | Download (5.7 kB)

1
#!/usr/bin/env python
2
#
3
# Copyright (C) 2012 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
"""Grow an existing Image file
21

    
22
This program grows an existing Image file which resides under a
23
shared directory.
24

    
25
It takes it's input from environment variables. Specifically the
26
following variables should be present:
27

    
28
 - VOL_NAME: The name of the Image to grow
29
 - VOL_SIZE: The current Image's size
30
 - VOL_NEW_SIZE: The new size of the Image after it's grown
31

    
32
Returns 0 upon successful grow, 1 upon failure
33

    
34
"""
35

    
36
import os
37
import sys
38

    
39
sys.path.insert(0, "/etc/ganeti/share")
40

    
41
from ganeti import utils
42

    
43

    
44
# Default shared directory
45
SHARED_DIR = "/srv/ganeti/shared-file-storage"
46

    
47

    
48
def ReadEnv():
49
  """Read the mandatory enviromental variables
50

    
51
  """
52
  name = os.getenv("VOL_NAME")
53
  if name is None:
54
    sys.stderr.write('The environment variable VOL_NAME is missing.\n')
55
    return None
56
  new_size = os.getenv("VOL_NEW_SIZE")
57
  if new_size is None:
58
    sys.stderr.write('The environment variable VOL_NEW_SIZE is missing.\n')
59
    return None
60
  shared_dir_param = os.getenv("EXTP_SHARED_DIR")
61
  if shared_dir_param is not None:
62
    sys.stderr.write('Found shared dir param, value: %s\n' % shared_dir_param)
63

    
64
  return (name, new_size, shared_dir_param)
65

    
66

    
67
def _ParseLosetupOutput(output, filename):
68
  """Parse the output of `losetup -j <filename>`
69

    
70
  This method parses the output of `losetup -j <filename>` and returns
71
  the corresponding loop device path for this filename
72
  (e.g. /dev/loop0)
73

    
74
  @type output: string
75
  @param output: the whole output of `losetup -j filename`
76
  @type filename: string
77
  @param filename: the complete file path to the file we search for
78
  @rtype: string or None
79
  @return: loop device path if the file is losetup, else None
80

    
81
  """
82
  # `losetup -j file1` output looks like this
83
  #   /dev/loop0: [X]:Y (/path/to/file1)
84
  # if the file has been losetup. If not there is no output.
85
  # If the file has been losetup more than once, then there will be
86
  # multiple lines like the one above.
87
  #
88
  # We assume the following format (space is the seperator):
89
  # field0: field1 (field2)
90

    
91
  field0 = 0
92
  field_sep = " "
93
  
94
  lines = output.splitlines()
95
  splitted_lines = map(lambda l: l.split(field_sep), lines)
96

    
97
  # Check empty output. If empty, the file is not losetup
98
  if not splitted_lines:
99
    return None
100

    
101
  # Check if we have more than one lines in the output
102
  if len(splitted_lines) > 1:
103
    sys.stderr.write("The file %s is losetup more than once" % filename)
104
    sys.exit(1)
105

    
106
  # The file is losetup only once
107
  # Take field0, remove the ":" at the end and we have our path
108
  dev_path = splitted_lines[0][field0].split(":")[0]
109

    
110
  return dev_path
111

    
112

    
113
def main():
114
  sys.stderr.write('Creation started: reading environment...\n')
115

    
116
  # Read environment.
117
  env = ReadEnv()
118
  if env is None:
119
    sys.stderr.write('Wrong environment. Aborting\n')
120
    return 1
121

    
122
  file_name, new_size, shared_dir_param = env
123

    
124
  if shared_dir_param is None:
125
    shared_dir_param = SHARED_DIR
126

    
127
  file_path = utils.PathJoin(shared_dir_param, file_name)
128

    
129
  sys.stderr.write('Read environment successfully: file name: %s, '
130
                   'new size: %sMB\n' % (file_path, new_size))
131

    
132
  # Check if the file exists.
133
  if not os.path.exists(file_path):
134
    sys.stderr.write("File %s does not exist. Aborting\n" % file_path)
135
    return 1
136

    
137
  # Grow file.
138
  try:
139
    f = open(file_path, "a+")
140
    f.truncate(int(new_size) * 1024 * 1024)
141
    f.close()
142
  except EnvironmentError, err:
143
    sys.stderr.write('Error in file growth: %' % str(err))
144
    return 1
145

    
146
  # Growth successful.
147
  # Now the loop device needs to reread the size of the associated file.
148

    
149
  # Find out the loop device path first.
150
  cmd1 = ["losetup", "-j", "%s" % file_path]
151
  result = utils.RunCmd(cmd1)
152
  if result.failed:
153
    sys.stderr.write("losetup -j %s failed (%s): %s" %
154
                     (dev_path, result.fail_reason, result.output))
155
    return 1
156
  else:
157
    # Parse the result's output.
158
    dev_path = _ParseLosetupOutput(result.output, file_path)
159

    
160
    if dev_path is None:
161
      # The file is not attached. We should never get here during growth.
162
      sys.stderr.write('File %s is not losetup during growth (%s): %s\n'
163
                       'This should not happen. Aborting\n' %
164
                        file_path, result.fail_reason, result.output)
165
      return 1
166

    
167
    else:
168
      # The file is losetup as expected. Reread size of file.
169
      cmd2 = ["losetup", "-c", "%s" % dev_path]
170
      result = utils.RunCmd(cmd2)
171
      if result.failed:
172
        sys.stderr.write('losetup -c %s failed (%s): %s' %
173
                         (dev_path, result.fail_reason, result.output))
174
        sys.stderr.write('You have to manually reread size of loop device `%s` '
175
                         'since its associated file `%s` has grown\n' %
176
                         (dev_path, file_path,
177
                          result.fail_reason, result.output))
178
        return 1
179

    
180
      # Growth and reread successful.
181
      sys.stderr.write('Grow successful for file: %s\n' % file_path)
182
      return 0
183

    
184

    
185
if __name__ == "__main__":
186
    sys.exit(main())