-my $confvars = {
- path => 'path',
- shared => 'bool',
- disable => 'bool',
- saferemove => 'bool',
- format => 'format',
- content => 'content',
- server => 'server',
- export => 'path',
- vgname => 'vgname',
- base => 'volume',
- portal => 'portal',
- target => 'target',
- nodes => 'nodes',
- options => 'options',
- maxfiles => 'natural',
-};
-
-my $required_config = {
- dir => ['path'],
- nfs => ['path', 'server', 'export'],
- lvm => ['vgname'],
- iscsi => ['portal', 'target'],
-};
-
-my $fixed_config = {
- dir => ['path'],
- nfs => ['path', 'server', 'export'],
- lvm => ['vgname', 'base'],
- iscsi => ['portal', 'target'],
-};
-
-my $default_config = {
- dir => {
- path => 1,
- nodes => 0,
- shared => 0,
- disable => 0,
- maxfiles => 0,
- content => [ { images => 1, rootdir => 1, vztmpl => 1, iso => 1, backup => 1, none => 1 },
- { images => 1, rootdir => 1 }],
- format => [ { raw => 1, qcow2 => 1, vmdk => 1 } , 'raw' ],
- },
-
- nfs => {
- path => 1,
- nodes => 0,
- disable => 0,
- server => 1,
- export => 1,
- options => 0,
- maxfiles => 0,
- content => [ { images => 1, rootdir => 1, vztmpl => 1, iso => 1, backup => 1},
- { images => 1 }],
- format => [ { raw => 1, qcow2 => 1, vmdk => 1 } , 'raw' ],
- },
-
- lvm => {
- vgname => 1,
- nodes => 0,
- shared => 0,
- disable => 0,
- saferemove => 0,
- content => [ {images => 1}, { images => 1 }],
- base => 1,
- },
-
- iscsi => {
- portal => 1,
- target => 1,
- nodes => 0,
- disable => 0,
- content => [ {images => 1, none => 1}, { images => 1 }],
- },
-};
-
-sub valid_content_types {
- my ($stype) = @_;
-
- my $def = $default_config->{$stype};
-
- return {} if !$def;
-
- return $def->{content}->[0];
-}
-
-sub content_hash_to_string {
- my $hash = shift;
-
- my @cta;
- foreach my $ct (keys %$hash) {
- push @cta, $ct if $hash->{$ct};
- }
-
- return join(',', @cta);
-}
-
-PVE::JSONSchema::register_format('pve-storage-path', \&verify_path);
-sub verify_path {
- my ($path, $noerr) = @_;
-
- # fixme: exclude more shell meta characters?
- # we need absolute paths
- if ($path !~ m|^/[^;\(\)]+|) {
- return undef if $noerr;
- die "value does not look like a valid absolute path\n";
- }
- return $path;
-}
-
-PVE::JSONSchema::register_format('pve-storage-server', \&verify_server);
-sub verify_server {
- my ($server, $noerr) = @_;
-
- # fixme: use better regex ?
- # IP or DNS name
- if ($server !~ m/^[[:alnum:]\-\.]+$/) {
- return undef if $noerr;
- die "value does not look like a valid server name or IP address\n";
- }
- return $server;
-}
-
-PVE::JSONSchema::register_format('pve-storage-portal', \&verify_portal);
-sub verify_portal {
- my ($portal, $noerr) = @_;
-
- # IP with optional port
- if ($portal !~ m/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}(:\d+)?$/) {
- return undef if $noerr;
- die "value does not look like a valid portal address\n";
- }
- return $portal;
-}
-
-PVE::JSONSchema::register_format('pve-storage-portal-dns', \&verify_portal_dns);
-sub verify_portal_dns {
- my ($portal, $noerr) = @_;
-
- # IP or DNS name with optional port
- if ($portal !~ m/^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|[[:alnum:]\-\.]+)(:\d+)?$/) {
- return undef if $noerr;
- die "value does not look like a valid portal address\n";
- }
- return $portal;
-}
-
-PVE::JSONSchema::register_format('pve-storage-content', \&verify_content);
-sub verify_content {
- my ($ct, $noerr) = @_;
-
- my $valid_content = valid_content_types('dir'); # dir includes all types
-
- if (!$valid_content->{$ct}) {
- return undef if $noerr;
- die "invalid content type '$ct'\n";
- }
-
- return $ct;
-}
-
-PVE::JSONSchema::register_format('pve-storage-format', \&verify_format);
-sub verify_format {
- my ($fmt, $noerr) = @_;
-
- if ($fmt !~ m/(raw|qcow2|vmdk)/) {
- return undef if $noerr;
- die "invalid format '$fmt'\n";
- }
-
- return $fmt;
-}
-
-PVE::JSONSchema::register_format('pve-storage-options', \&verify_options);
-sub verify_options {
- my ($value, $noerr) = @_;
-
- # mount options (see man fstab)
- if ($value !~ m/^\S+$/) {
- return undef if $noerr;
- die "invalid options '$value'\n";
- }
-
- return $value;
-}
-
-sub check_type {
- my ($stype, $ct, $key, $value, $storeid, $noerr) = @_;
-
- my $def = $default_config->{$stype};
-
- if (!$def) { # should not happen
- return undef if $noerr;
- die "unknown storage type '$stype'\n";
- }
-
- if (!defined($def->{$key})) {
- return undef if $noerr;
- die "unexpected property\n";
- }
-
- if (!defined ($value)) {
- return undef if $noerr;
- die "got undefined value\n";
- }
-
- if ($value =~ m/[\n\r]/) {
- return undef if $noerr;
- die "property contains a line feed\n";
- }
-
- if ($ct eq 'bool') {
- return 1 if ($value eq '1') || ($value =~ m/^(on|yes|true)$/i);
- return 0 if ($value eq '0') || ($value =~ m/^(off|no|false)$/i);
- return undef if $noerr;
- die "type check ('boolean') failed - got '$value'\n";
- } elsif ($ct eq 'options') {
- return verify_options($value, $noerr);
- } elsif ($ct eq 'path') {
- return verify_path($value, $noerr);
- } elsif ($ct eq 'server') {
- return verify_server($value, $noerr);
- } elsif ($ct eq 'vgname') {
- return parse_lvm_name ($value, $noerr);
- } elsif ($ct eq 'portal') {
- return verify_portal($value, $noerr);
- } elsif ($ct eq 'natural') {
- return int($value) if $value =~ m/^\d+$/;
- return undef if $noerr;
- die "type check ('natural') failed - got '$value'\n";
- } elsif ($ct eq 'nodes') {
- my $res = {};
-
- foreach my $node (PVE::Tools::split_list($value)) {
- if (PVE::JSONSchema::pve_verify_node_name($node, $noerr)) {
- $res->{$node} = 1;
- }
- }
-
- # no node restrictions for local storage
- if ($storeid && $storeid eq 'local' && scalar(keys(%$res))) {
- return undef if $noerr;
- die "storage '$storeid' does not allow node restrictions\n";
- }
-
- return $res;
- } elsif ($ct eq 'target') {
- return $value;
- } elsif ($ct eq 'string') {
- return $value;
- } elsif ($ct eq 'format') {
- my $valid_formats = $def->{format}->[0];
-
- if (!$valid_formats->{$value}) {
- return undef if $noerr;
- die "storage does not support format '$value'\n";
- }
-
- return $value;
-
- } elsif ($ct eq 'content') {
- my $valid_content = $def->{content}->[0];
-
- my $res = {};
-
- foreach my $c (PVE::Tools::split_list($value)) {
- if (!$valid_content->{$c}) {
- return undef if $noerr;
- die "storage does not support content type '$c'\n";
- }
- $res->{$c} = 1;
- }
-
- if ($res->{none} && scalar (keys %$res) > 1) {
- return undef if $noerr;
- die "unable to combine 'none' with other content types\n";
- }
-
- return $res;
- } elsif ($ct eq 'volume') {
- return $value if parse_volume_id ($value, $noerr);
- }
-
- return undef if $noerr;
- die "type check not implemented - internal error\n";
-}
-
-sub parse_config {
- my ($filename, $raw) = @_;
-
- my $ids = {};
-
- my $digest = Digest::SHA::sha1_hex(defined($raw) ? $raw : '');
-
- my $pri = 0;
-
- while ($raw && $raw =~ s/^(.*?)(\n|$)//) {
- my $line = $1;
-
- next if $line =~ m/^\#/;
- next if $line =~ m/^\s*$/;
-
- if ($line =~ m/^(\S+):\s*(\S+)\s*$/) {
- my $storeid = $2;
- my $type = $1;
- my $ignore = 0;
-
- if (!PVE::JSONSchema::parse_storage_id($storeid, 1)) {
- $ignore = 1;
- warn "ignoring storage '$storeid' - (illegal characters)\n";
- } elsif (!$default_config->{$type}) {
- $ignore = 1;
- warn "ignoring storage '$storeid' (unsupported type '$type')\n";
- } else {
- $ids->{$storeid}->{type} = $type;
- $ids->{$storeid}->{priority} = $pri++;
- }
-
- while ($raw && $raw =~ s/^(.*?)(\n|$)//) {
- $line = $1;
-
- next if $line =~ m/^\#/;
- last if $line =~ m/^\s*$/;
-
- next if $ignore; # skip
-
- if ($line =~ m/^\s+(\S+)(\s+(.*\S))?\s*$/) {
- my ($k, $v) = ($1, $3);
- if (my $ct = $confvars->{$k}) {
- $v = 1 if $ct eq 'bool' && !defined($v);
- eval {
- $ids->{$storeid}->{$k} = check_type ($type, $ct, $k, $v, $storeid);
- };
- warn "storage '$storeid' - unable to parse value of '$k': $@" if $@;
- } else {
- warn "storage '$storeid' - unable to parse value of '$k'\n";
- }
-
- } else {
- warn "storage '$storeid' - ignore config line: $line\n";
- }
- }
- } else {
- warn "ignore config line: $line\n";
- }
- }
-
- # make sure we have a reasonable 'local:' storage
- # openvz expects things to be there
- if (!$ids->{local} || $ids->{local}->{type} ne 'dir' ||
- $ids->{local}->{path} ne '/var/lib/vz') {
- $ids->{local} = {
- type => 'dir',
- priority => $pri++,
- path => '/var/lib/vz',
- maxfiles => 0,
- content => { images => 1, rootdir => 1, vztmpl => 1, iso => 1},
- };
- }
-
- # we always need this for OpenVZ
- $ids->{local}->{content}->{rootdir} = 1;
- $ids->{local}->{content}->{vztmpl} = 1;
- delete ($ids->{local}->{disable});
-
- # remove node restrictions for local storage
- delete($ids->{local}->{nodes});
-
- foreach my $storeid (keys %$ids) {
- my $d = $ids->{$storeid};
-
- my $req_keys = $required_config->{$d->{type}};
- foreach my $k (@$req_keys) {
- if (!defined ($d->{$k})) {
- warn "ignoring storage '$storeid' - missing value " .
- "for required option '$k'\n";
- delete $ids->{$storeid};
- next;
- }
- }
-
- my $def = $default_config->{$d->{type}};
-
- if ($def->{content}) {
- $d->{content} = $def->{content}->[1] if !$d->{content};
- }
-
- if ($d->{type} eq 'iscsi' || $d->{type} eq 'nfs') {
- $d->{shared} = 1;
- }
- }
-
- my $cfg = { ids => $ids, digest => $digest};
-
- return $cfg;
-}
-
-sub parse_options {
- my ($storeid, $stype, $param, $create) = @_;
-
- my $settings = { type => $stype };
-
- die "unknown storage type '$stype'\n"
- if !$default_config->{$stype};
-
- foreach my $opt (keys %$param) {
- my $value = $param->{$opt};
-
- my $ct = $confvars->{$opt};
- if (defined($value)) {
- eval {
- $settings->{$opt} = check_type ($stype, $ct, $opt, $value, $storeid);
- };
- raise_param_exc({ $opt => $@ }) if $@;
- } else {
- raise_param_exc({ $opt => "got undefined value" });
- }
- }
-
- if ($create) {
- my $req_keys = $required_config->{$stype};
- foreach my $k (@$req_keys) {
-
- if ($stype eq 'nfs' && !$settings->{path}) {
- $settings->{path} = "/mnt/pve/$storeid";
- }
-
- # check if we have a value for all required options
- if (!defined ($settings->{$k})) {
- raise_param_exc({ $k => "property is missing and it is not optional" });
- }
- }
- } else {
- my $fixed_keys = $fixed_config->{$stype};
- foreach my $k (@$fixed_keys) {
-
- # only allow to change non-fixed values
-
- if (defined ($settings->{$k})) {
- raise_param_exc({$k => "can't change value (fixed parameter)"});
- }
- }
- }
-
- return $settings;
-}
-
-sub cluster_lock_storage {
- my ($storeid, $shared, $timeout, $func, @param) = @_;
-
- my $res;
- if (!$shared) {
- my $lockid = "pve-storage-$storeid";
- my $lockdir = "/var/lock/pve-manager";
- mkdir $lockdir;
- $res = PVE::Tools::lock_file("$lockdir/$lockid", $timeout, $func, @param);
- die $@ if $@;
- } else {
- $res = PVE::Cluster::cfs_lock_storage($storeid, $timeout, $func, @param);
- die $@ if $@;
- }
- return $res;
-}
-