Deactivate and reactivate LVs
[invirt/packages/invirt-remote.git] / host / usr / sbin / invirt-lvm
1 #!/usr/bin/env python
2
3 import sys
4 import time
5 import os
6 import random
7 import string
8 from subprocess import call, PIPE, Popen
9 from invirt.config import structs as config
10
11 def check(b):
12     if not b:
13         exit(1)
14
15 vg = "xenvg"
16 prefix = "d_"
17
18 subcommand = sys.argv[1]
19
20 def ensureoff(machine):
21     # Make sure the machine is off, but we don't care about errors if it is already off.
22     rv = call(["/usr/sbin/xm", "destroy", prefix + machine],
23               stderr=PIPE)
24
25 def lvm_activation(path, mode):
26     p = Popen(["/sbin/lvchange", "-a%s" % (mode,), path], stdout=PIPE, stderr=PIPE)
27     rv = p.wait()
28     return rv
29
30 machine_specific = subcommand not in ['lvcreate-all', 'vgcapacity']
31
32 if machine_specific:
33     machine = sys.argv[2]
34     disk = sys.argv[3]
35     lvname = prefix + machine + "_" + disk
36     lvpath = "/dev/" + vg + "/" + lvname
37
38 if subcommand == "lvcreate-all":
39     from invirt.database import models, connect
40     import re
41     connect()
42     for d in models.Disk.query().all():
43         check(re.match('^[A-Za-z0-9]+$', d.guest_device_name))
44         machine = models.Machine.query().filter_by(machine_id=d.machine_id).one()
45         check(re.match('^[A-Za-z0-9][A-Za-z0-9._-]*$', machine.name))
46         lvname = prefix + machine.name + "_" + d.guest_device_name
47         if not os.path.exists("/dev/%s/%s" % (vg, lvname)):
48             # LV doesn't exist
49             print >>sys.stderr, "Creating LV %s..." % (lvname,)
50             rv = call(["/sbin/lvcreate", "-L", str(d.size) + "M", "-n", lvname, vg])
51             if rv != 0:
52                 print >>sys.stderr, "Error creating LV %s\n" %(lvname,)
53                 sys.exit(1)
54 elif subcommand == "lvremove":
55     def error():
56         print >>sys.stderr, "Error removing LV %s\n" % lvname
57         sys.exit(1)
58     
59     # Rename the LV to something else so we can wipe it before reusing
60     # the space
61     if lvm_activation(lvpath, 'n') != 0:
62         print >>sys.stderr, "Could not deactivate LV %s", % (lvname,)
63         sys.exit(1)
64     while True:
65         new_lvname = "old_%s_%s" % (lvname, ''.join(random.choice(string.ascii_letters) for i in xrange(6)))
66         new_lvpath = "/dev/%s/%s" % (vg, new_lvname)
67         p = Popen(["/sbin/lvrename", lvpath, new_lvpath], stdout=PIPE, stderr=PIPE)
68         rv = p.wait()
69         if rv == 5 and 'already exists in volume group' in p.stderr.read():
70             continue
71         elif rv != 0:
72             error()
73         else:
74             if lvm_activation(new_lvpath, 'y') != 0:
75                 print >> sys.stderr, "Could not reactivate renamed LV %s" % (lvname,)
76                 sys.exit(1)
77             break
78     ensureoff(machine)
79     
80     # Touch a file corresponding to the new name of the LV; a separate
81     # daemon will handle wiping and deleting it.
82     open(os.path.join('/var/lib/invirt-remote/cleanup', new_lvname), 'w')
83 elif subcommand == "lvresize":
84     size = sys.argv[4]
85     ensureoff(machine)
86     if lvm_activation(lvpath, 'n') != 0:
87         print >>sys.stderr, "Could not deactivate LV %s", % (lvname,)
88         sys.exit(1)
89     p = Popen(["/sbin/lvresize", "-L", size + "M", lvpath],
90               stdin=PIPE, stderr=PIPE)
91     print >> p.stdin, 'y'
92     err = p.stderr.read()
93     if p.wait() != 0 and 'matches existing size' not in err:
94         print >> sys.stderr, "Error resizing LV %s:\n" %(lvname,)
95         print >> sys.stderr, err
96         sys.exit(1)
97     if lvm_activation(lvpath, 'y') != 0:
98         print >> sys.stderr, "Could not reactivate resized LV %s" % (lvname,)
99         sys.exit(1)
100     print >> sys.stderr, err
101 elif subcommand == "lvrename":
102     newmachine = sys.argv[4]
103     newlvname = prefix + newmachine + "_" + disk
104     ensureoff(machine)
105     ensureoff(newmachine)
106     lvpath = "/dev/" + vg + "/" + lvname
107     new_lvpath = "/dev/" + vg + "/" + newlvname
108     if lvm_activation(lvpath, 'n') != 0:
109         print >>sys.stderr, "Could not deactivate LV %s", % (lvname,)
110         sys.exit(1)
111     rv = call(["/sbin/lvrename", vg, lvname, newlvname])
112     if rv != 0:
113         print >>sys.stderr, "Error renaming LV %s\n" %(lvname,)
114         sys.exit(1)
115     if lvm_activation(new_lvpath, 'y') != 0:
116         print >> sys.stderr, "Could not reactivate renamed LV %s" % (lvname,)
117         sys.exit(1)
118 elif subcommand == "lvcreate":
119     size = sys.argv[4]
120     rv = call(["/sbin/lvcreate", "-L", size + "M", "-n", lvname, vg])
121     if rv != 0:
122         print >>sys.stderr, "Error creating LV %s\n" %(lvname,)
123         sys.exit(1)
124 elif subcommand == "vgcapacity":
125     p = Popen(["/sbin/vgs", "-o", "vg_extent_size,vg_extent_count,vg_free_count", 
126                             "--noheadings", "--units", "k", "--nosuffix", "--separator", ":", 
127                             vg],
128               stdout=PIPE, stderr=PIPE)
129     out,err = p.communicate()
130     
131     try:
132         fields = out.strip().split(':')
133         extent_size = float(fields[0]) # in kibibytes
134         extent_count = int(fields[1])
135         free_count = int(fields[2])
136         total_space_TiB = extent_size * extent_count / 1024.**3
137         free_space_TiB = extent_size * free_count / 1024.**3
138         print >>sys.stdout, "Total: %.3f TiB" % (total_space_TiB,)
139         print >>sys.stdout, "Free: %.3f TiB" % (free_space_TiB,)
140     except:
141         print >>sys.stderr, "Error obtaining vg capacity:\n%s\n" % (err,)
142         sys.exit(1)
143