]> git.proxmox.com Git - pve-storage.git/blame - PVE/Storage/GlusterfsPlugin.pm
Storage Plugins: extend clone_image by snap parameter and add support to RBDPlugin
[pve-storage.git] / PVE / Storage / GlusterfsPlugin.pm
CommitLineData
f4648aef
AD
1package PVE::Storage::GlusterfsPlugin;
2
3use strict;
4use warnings;
5use IO::File;
6use File::Path;
7use PVE::Tools qw(run_command);
8use PVE::Storage::Plugin;
9use PVE::JSONSchema qw(get_standard_option);
10use Net::Ping;
11
12use base qw(PVE::Storage::Plugin);
13
14# Glusterfs helper functions
15
16sub read_proc_mounts {
1a3459ac 17
f4648aef 18 local $/; # enable slurp mode
1a3459ac 19
f4648aef
AD
20 my $data = "";
21 if (my $fd = IO::File->new("/proc/mounts", "r")) {
22 $data = <$fd>;
23 close ($fd);
24 }
25
26 return $data;
27}
28
29sub glusterfs_is_mounted {
30 my ($server, $volume, $mountpoint, $mountdata) = @_;
31
32 my $source = "$server:$volume";
33
34 $mountdata = read_proc_mounts() if !$mountdata;
35
36 if ($mountdata =~ m|^$source/?\s$mountpoint\sfuse.glusterfs|m) {
37 return $mountpoint;
1a3459ac 38 }
f4648aef
AD
39
40 return undef;
41}
42
43sub glusterfs_mount {
44 my ($server, $volume, $mountpoint) = @_;
45
46 my $source = "$server:$volume";
47
48 my $cmd = ['/bin/mount', '-t', 'glusterfs', $source, $mountpoint];
49
50 run_command($cmd, errmsg => "mount error");
51}
52
53# Configuration
54
55sub type {
56 return 'glusterfs';
57}
58
59sub plugindata {
60 return {
61 content => [ { images => 1, rootdir => 1, vztmpl => 1, iso => 1, backup => 1},
62 { images => 1 }],
63 format => [ { raw => 1, qcow2 => 1, vmdk => 1 } , 'raw' ],
64 };
1a3459ac 65}
f4648aef
AD
66
67sub properties {
68 return {
69 volume => {
70 description => "Glusterfs Volume.",
71 type => 'string',
72 },
73 };
74}
75
76sub options {
77 return {
78 path => { fixed => 1 },
79 server => { optional => 1 },
80 volume => { fixed => 1 },
81 nodes => { optional => 1 },
82 disable => { optional => 1 },
83 maxfiles => { optional => 1 },
84 content => { optional => 1 },
85 format => { optional => 1 },
86 };
87}
88
89
90sub check_config {
91 my ($class, $sectionId, $config, $create, $skipSchemaCheck) = @_;
92
93 $config->{path} = "/mnt/pve/$sectionId" if $create && !$config->{path};
94
95 return $class->SUPER::check_config($sectionId, $config, $create, $skipSchemaCheck);
96}
97
98# Storage implementation
99
db7922dc
AD
100sub parse_name_dir {
101 my $name = shift;
102
103 if ($name =~ m!^((base-)?[^/\s]+\.(raw|qcow2|vmdk))$!) {
104 return ($1, $3, $2);
105 }
106
107 die "unable to parse volume filename '$name'\n";
108}
109
110my $find_free_diskname = sub {
111 my ($imgdir, $vmid, $fmt) = @_;
112
113 my $disk_ids = {};
114 PVE::Tools::dir_glob_foreach($imgdir,
115 qr!(vm|base)-$vmid-disk-(\d+)\..*!,
116 sub {
117 my ($fn, $type, $disk) = @_;
118 $disk_ids->{$disk} = 1;
119 });
120
121 for (my $i = 1; $i < 100; $i++) {
122 if (!$disk_ids->{$i}) {
123 return "vm-$vmid-disk-$i.$fmt";
124 }
125 }
126
127 die "unable to allocate a new image name for VM $vmid in '$imgdir'\n";
128};
129
f4648aef
AD
130sub path {
131 my ($class, $scfg, $volname, $storeid) = @_;
132
133 my ($vtype, $name, $vmid) = $class->parse_volname($volname);
134
135 my $server = $scfg->{server} ? $scfg->{server} : 'localhost';
136 my $glustervolume = $scfg->{volume};
137
138 my $path = undef;
139 if($vtype eq 'images'){
140 $path = "gluster://$server/$glustervolume/images/$vmid/$name";
141 }else{
142 my $dir = $class->get_subdir($scfg, $vtype);
143 $path = "$dir/$name";
144 }
145
146
147 return wantarray ? ($path, $vmid, $vtype) : $path;
148}
149
db7922dc
AD
150sub alloc_image {
151 my ($class, $storeid, $scfg, $vmid, $fmt, $name, $size) = @_;
152
153 my $imagedir = $class->get_subdir($scfg, 'images');
154 $imagedir .= "/$vmid";
155
156 mkpath $imagedir;
157
158 $name = &$find_free_diskname($imagedir, $vmid, $fmt) if !$name;
159
160 my (undef, $tmpfmt) = parse_name_dir($name);
161
162 die "illegal name '$name' - wrong extension for format ('$tmpfmt != '$fmt')\n"
163 if $tmpfmt ne $fmt;
164
165 my $path = "$imagedir/$name";
166
167 die "disk image '$path' already exists\n" if -e $path;
168
169 my $server = $scfg->{server} ? $scfg->{server} : 'localhost';
170 my $glustervolume = $scfg->{volume};
171 my $volumepath = "gluster://$server/$glustervolume/images/$vmid/$name";
172
173 my $cmd = ['/usr/bin/qemu-img', 'create'];
174
175 push @$cmd, '-o', 'preallocation=metadata' if $fmt eq 'qcow2';
176
177 push @$cmd, '-f', $fmt, $volumepath, "${size}K";
178
179 run_command($cmd, errmsg => "unable to create image");
180
181 return "$vmid/$name";
182}
183
f4648aef
AD
184sub status {
185 my ($class, $storeid, $scfg, $cache) = @_;
186
187 $cache->{mountdata} = read_proc_mounts() if !$cache->{mountdata};
188
189 my $path = $scfg->{path};
190 my $server = $scfg->{server} ? $scfg->{server} : 'localhost';
191
192 my $volume = $scfg->{volume};
193
1a3459ac 194 return undef if !glusterfs_is_mounted($server, $volume, $path, $cache->{mountdata});
f4648aef
AD
195
196 return $class->SUPER::status($storeid, $scfg, $cache);
197}
198
199sub activate_storage {
200 my ($class, $storeid, $scfg, $cache) = @_;
201
202 $cache->{mountdata} = read_proc_mounts() if !$cache->{mountdata};
203
204 my $path = $scfg->{path};
205 my $server = $scfg->{server} ? $scfg->{server} : 'localhost';
206 my $volume = $scfg->{volume};
207
1a3459ac
DM
208 if (!glusterfs_is_mounted($server, $volume, $path, $cache->{mountdata})) {
209
f4648aef
AD
210 mkpath $path;
211
212 die "unable to activate storage '$storeid' - " .
213 "directory '$path' does not exist\n" if ! -d $path;
214
215 glusterfs_mount($server, $volume, $path, $scfg->{options});
216 }
217
218 $class->SUPER::activate_storage($storeid, $scfg, $cache);
219}
220
221sub deactivate_storage {
222 my ($class, $storeid, $scfg, $cache) = @_;
223
224 $cache->{mountdata} = read_proc_mounts() if !$cache->{mountdata};
225
226 my $path = $scfg->{path};
227 my $server = $scfg->{server} ? $scfg->{server} : 'localhost';
228 my $volume = $scfg->{volume};
229
1a3459ac 230 if (glusterfs_is_mounted($server, $volume, $path, $cache->{mountdata})) {
f4648aef 231 my $cmd = ['/bin/umount', $path];
1a3459ac 232 run_command($cmd, errmsg => 'umount error');
f4648aef
AD
233 }
234}
235
236sub activate_volume {
237 my ($class, $storeid, $scfg, $volname, $exclusive, $cache) = @_;
238
239 # do nothing by default
240}
241
242sub deactivate_volume {
243 my ($class, $storeid, $scfg, $volname, $cache) = @_;
244
245 # do nothing by default
246}
247
248sub check_connection {
249 my ($class, $storeid, $scfg) = @_;
250
251 my $server = $scfg->{server} ? $scfg->{server} : 'localhost';
252 my $volume = $scfg->{volume};
253
254 my $status = 0;
255
256 if($server && $server ne 'localhost' && $server ne '127.0.0.1'){
257 my $p = Net::Ping->new("tcp", 2);
258 $status = $p->ping($server);
259
260 }else{
261
262 my $parser = sub {
263 my $line = shift;
264
265 if ($line =~ m/Status: Started$/) {
266 $status = 1;
267 }
268 };
269
270 my $cmd = ['/usr/sbin/gluster', 'volume', 'info', $volume];
271
272 run_command($cmd, errmsg => "glusterfs error", errfunc => sub {}, outfunc => $parser);
273 }
274
275 return $status;
276}
277
2781;