]> git.proxmox.com Git - pve-cluster.git/blobdiff - data/PVE/Cluster.pm
mac_prefix: do not allow multicast prefixes
[pve-cluster.git] / data / PVE / Cluster.pm
index 4e3bf7ab8cb7b16853bdc07404ad8c341ff77401..5c71c1ceff5d3f82d94324f110bacf4d3e53eebe 100644 (file)
@@ -17,6 +17,7 @@ use PVE::IPCC;
 use PVE::SafeSyslog;
 use PVE::JSONSchema;
 use PVE::Network;
+use PVE::Cluster::IPCConst;
 use JSON;
 use RRDs;
 use Encode;
@@ -66,6 +67,9 @@ my $rootsshauthkeys = "/root/.ssh/authorized_keys";
 my $rootsshauthkeysbackup = "${rootsshauthkeys}.org";
 my $rootsshconfig = "/root/.ssh/config";
 
+# this is just a readonly copy, the relevant one is in status.c from pmxcfs
+# observed files are the one we can get directly through IPCC, they are cached
+# using a computed version and only those can be used by the cfs_*_file methods
 my $observed = {
     'vzdump.cron' => 1,
     'storage.cfg' => 1,
@@ -85,6 +89,7 @@ my $observed = {
     'ha/groups.cfg' => 1,
     'ha/fence.cfg' => 1,
     'status.cfg' => 1,
+    'ceph.conf' => 1,
 };
 
 # only write output if something fails
@@ -92,20 +97,11 @@ sub run_silent_cmd {
     my ($cmd) = @_;
 
     my $outbuf = '';
+    my $record = sub { $outbuf .= shift . "\n"; };
 
-    my $record_output = sub {
-       $outbuf .= shift;
-       $outbuf .= "\n";
-    };
-
-    eval {
-       PVE::Tools::run_command($cmd, outfunc => $record_output,
-                               errfunc => $record_output);
-    };
+    eval { run_command($cmd, outfunc => $record, errfunc => $record) };
 
-    my $err = $@;
-
-    if ($err) {
+    if (my $err = $@) {
        print STDERR $outbuf;
        die $err;
     }
@@ -408,7 +404,7 @@ my $ipcc_get_config = sub {
     my ($path) = @_;
 
     my $bindata = pack "Z*", $path;
-    my $res = PVE::IPCC::ipcc_send_rec(6, $bindata);
+    my $res = PVE::IPCC::ipcc_send_rec(CFS_IPC_GET_CONFIG, $bindata);
     if (!defined($res)) {
        if ($! != 0) {
            return undef if $! == ENOENT;
@@ -424,7 +420,7 @@ my $ipcc_get_status = sub {
     my ($name, $nodename) = @_;
 
     my $bindata = pack "Z[256]Z[256]", $name, ($nodename || "");
-    return PVE::IPCC::ipcc_send_rec(5, $bindata);
+    return PVE::IPCC::ipcc_send_rec(CFS_IPC_GET_STATUS, $bindata);
 };
 
 my $ipcc_update_status = sub {
@@ -434,7 +430,7 @@ my $ipcc_update_status = sub {
     # update status
     my $bindata = pack "Z[256]Z*", $name, $raw;
 
-    return &$ipcc_send_rec(4, $bindata);
+    return &$ipcc_send_rec(CFS_IPC_SET_STATUS, $bindata);
 };
 
 my $ipcc_log = sub {
@@ -443,7 +439,7 @@ my $ipcc_log = sub {
     my $bindata = pack "CCCZ*Z*Z*", $priority, bytes::length($ident) + 1,
     bytes::length($tag) + 1, $ident, $tag, $msg;
 
-    return &$ipcc_send_rec(7, $bindata);
+    return &$ipcc_send_rec(CFS_IPC_LOG_CLUSTER_MSG, $bindata);
 };
 
 my $ipcc_get_cluster_log = sub {
@@ -452,7 +448,7 @@ my $ipcc_get_cluster_log = sub {
     $max = 0 if !defined($max);
 
     my $bindata = pack "VVVVZ*", $max, 0, 0, 0, ($user || "");
-    return &$ipcc_send_rec(8, $bindata);
+    return &$ipcc_send_rec(CFS_IPC_GET_CLUSTER_LOG, $bindata);
 };
 
 my $ccache = {};
@@ -460,7 +456,7 @@ my $ccache = {};
 sub cfs_update {
     my ($fail) = @_;
     eval {
-       my $res = &$ipcc_send_rec_json(1);
+       my $res = &$ipcc_send_rec_json(CFS_IPC_GET_FS_VERSION);
        #warn "GOT1: " . Dumper($res);
        die "no starttime\n" if !$res->{starttime};
 
@@ -487,7 +483,7 @@ sub cfs_update {
     eval {
        if (!$clinfo->{version} || $clinfo->{version} != $versions->{clinfo}) {
            #warn "detected new clinfo\n";
-           $clinfo = &$ipcc_send_rec_json(2);
+           $clinfo = &$ipcc_send_rec_json(CFS_IPC_GET_CLUSTER_INFO);
        }
     };
     $err = $@;
@@ -500,7 +496,7 @@ sub cfs_update {
     eval {
        if (!$vmlist->{version} || $vmlist->{version} != $versions->{vmlist}) {
            #warn "detected new vmlist1\n";
-           $vmlist = &$ipcc_send_rec_json(3);
+           $vmlist = &$ipcc_send_rec_json(CFS_IPC_GET_GUEST_LIST);
        }
     };
     $err = $@;
@@ -524,11 +520,8 @@ sub get_members {
 }
 
 sub get_nodelist {
-
     my $nodelist = $clinfo->{nodelist};
 
-    my $result = [];
-
     my $nodename = PVE::INotify::nodename();
 
     if (!$nodelist || !$nodelist->{$nodename}) {
@@ -617,7 +610,7 @@ sub rrd_dump {
 
     my $raw;
     eval {
-       $raw = &$ipcc_send_rec(10);
+       $raw = &$ipcc_send_rec(CFS_IPC_GET_RRD_DUMP);
     };
     my $err = $@;
 
@@ -873,7 +866,7 @@ my $cfs_lock = sub {
     my $res;
     my $got_lock = 0;
 
-    # this timeout is for aquire the lock
+    # this timeout is for acquire the lock
     $timeout = 10 if !$timeout;
 
     my $filename = "$lockdir/$lockid";
@@ -898,7 +891,7 @@ my $cfs_lock = sub {
 
            $timeout_err->() if $timeout <= 0;
 
-           print STDERR "trying to aquire cfs lock '$lockid' ...\n";
+           print STDERR "trying to acquire cfs lock '$lockid' ...\n";
            utime (0, 0, $filename); # cfs unlock request
            sleep(1);
        }
@@ -1345,6 +1338,27 @@ my $migration_format = {
     },
 };
 
+my $ha_format = {
+    shutdown_policy => {
+       type => 'string',
+       enum => ['freeze', 'failover', 'conditional'],
+       description => "The policy for HA services on node shutdown. 'freeze' disables auto-recovery, 'failover' ensures recovery, 'conditional' recovers on poweroff and freezes on reboot. Running HA Services will always get stopped first on shutdown.",
+       verbose_description => "Describes the policy for handling HA services on poweroff or reboot of a node. Freeze will always freeze services which are still located on the node on shutdown, those services won't be recovered by the HA manager. Failover will not mark the services as frozen and thus the services will get recovered to other nodes, if the shutdown node does not come up again quickly (< 1min). 'conditional' chooses automatically depending on the type of shutdown, i.e., on a reboot the service will be frozen but on a poweroff the service will stay as is, and thus get recovered after about 2 minutes.",
+       default => 'conditional',
+    }
+};
+
+PVE::JSONSchema::register_format('mac-prefix', \&pve_verify_mac_prefix);
+sub pve_verify_mac_prefix {
+    my ($mac_prefix, $noerr) = @_;
+
+    if ($mac_prefix !~ m/^[a-f0-9][02468ace](?::[a-f0-9]{2}){0,2}:?$/i) {
+       return undef if $noerr;
+       die "value is not a valid unicast MAC address prefix\n";
+    }
+    return $mac_prefix;
+}
+
 my $datacenter_schema = {
     type => "object",
     additionalProperties => 0,
@@ -1359,7 +1373,27 @@ my $datacenter_schema = {
            optional => 1,
            type => 'string',
            description => "Default GUI language.",
-           enum => [ 'en', 'de' ],
+           enum => [
+               'zh_CN',
+               'zh_TW',
+               'ca',
+               'en',
+               'eu',
+               'fr',
+               'de',
+               'it',
+               'es',
+               'ja',
+               'nb',
+               'nn',
+               'fa',
+               'pl',
+               'pt_BR',
+               'ru',
+               'sl',
+               'sv',
+               'tr',
+           ],
        },
        http_proxy => {
            optional => 1,
@@ -1408,10 +1442,15 @@ my $datacenter_schema = {
              " With both all two modes are used." .
              "\n\nWARNING: 'hardware' and 'both' are EXPERIMENTAL & WIP",
        },
+       ha => {
+           optional => 1,
+           type => 'string', format => $ha_format,
+           description => "Cluster wide HA settings.",
+       },
        mac_prefix => {
            optional => 1,
            type => 'string',
-           pattern => qr/[a-f0-9]{2}(?::[a-f0-9]{2}){0,2}:?/i,
+           format => 'mac-prefix',
            description => 'Prefix for autogenerated MAC addresses.',
        },
        bwlimit => PVE::JSONSchema::get_standard_option('bwlimit'),
@@ -1430,6 +1469,10 @@ sub parse_datacenter_config {
        $res->{migration} = PVE::JSONSchema::parse_property_string($migration_format, $migration);
     }
 
+    if (my $ha = $res->{ha}) {
+       $res->{ha} = PVE::JSONSchema::parse_property_string($ha_format, $ha);
+    }
+
     # for backwards compatibility only, new migration property has precedence
     if (defined($res->{migration_unsecure})) {
        if (defined($res->{migration}->{type})) {
@@ -1466,6 +1509,10 @@ sub write_datacenter_config {
        $cfg->{migration} = PVE::JSONSchema::print_property_string($migration, $migration_format);
     }
 
+    if (my $ha = $cfg->{ha}) {
+       $cfg->{ha} = PVE::JSONSchema::print_property_string($ha, $ha_format);
+    }
+
     return PVE::JSONSchema::dump_config($datacenter_schema, $filename, $cfg);
 }
 
@@ -1532,10 +1579,9 @@ sub read_ssl_cert_fingerprint {
        or die "unable to read '$cert_path' - $!\n";
 
     my $cert = Net::SSLeay::PEM_read_bio_X509($bio);
-    if (!$cert) {
-       Net::SSLeay::BIO_free($bio);
-       die "unable to read certificate from '$cert_path'\n";
-    }
+    Net::SSLeay::BIO_free($bio);
+
+    die "unable to read certificate from '$cert_path'\n" if !$cert;
 
     my $fp = Net::SSLeay::X509_get_fingerprint($cert, 'sha256');
     Net::SSLeay::X509_free($cert);
@@ -1812,7 +1858,7 @@ sub join {
        $conn_args->{manual_verification} = 1;
     }
 
-    print "Etablishing API connection with host '$host'\n";
+    print "Establishing API connection with host '$host'\n";
 
     my $conn = PVE::APIClient::LWP->new(%$conn_args);
     $conn->login();