my $sshglobalknownhosts = "/etc/ssh/ssh_known_hosts";
my $sshknownhosts = "/etc/pve/priv/known_hosts";
my $sshauthkeys = "/etc/pve/priv/authorized_keys";
+my $sshd_config_fn = "/etc/ssh/sshd_config";
my $rootsshauthkeys = "/root/.ssh/authorized_keys";
my $rootsshauthkeysbackup = "${rootsshauthkeys}.org";
my $rootsshconfig = "/root/.ssh/config";
'ha/manager_status' => 1,
'ha/resources.cfg' => 1,
'ha/groups.cfg' => 1,
+ 'ha/fence.cfg' => 1,
+ 'status.cfg' => 1,
};
# only write output if something fails
return if -f $pveca_key_fn;
eval {
- run_silent_cmd(['openssl', 'genrsa', '-out', $pveca_key_fn, '2048']);
+ run_silent_cmd(['openssl', 'genrsa', '-out', $pveca_key_fn, '4096']);
};
die "unable to generate pve ca key:\n$@" if $@;
my $nid = (split (/\s/, `md5sum '$pveca_key_fn'`))[0] || time();
eval {
- run_silent_cmd(['openssl', 'req', '-batch', '-days', '3650', '-new',
- '-x509', '-nodes', '-key',
+ # wrap openssl with faketime to prevent bug #904
+ run_silent_cmd(['faketime', 'yesterday', 'openssl', 'req', '-batch',
+ '-days', '3650', '-new', '-x509', '-nodes', '-key',
$pveca_key_fn, '-out', $pveca_cert_fn, '-subj',
"/CN=Proxmox Virtual Environment/OU=$nid/O=PVE Cluster Manager CA/"]);
};
[ v3_req ]
basicConstraints = CA:FALSE
-nsCertType = server
-keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+extendedKeyUsage = serverAuth
subjectAltName = $names
__EOD
update_serial("0000000000000000") if ! -f $pveca_srl_fn;
eval {
- run_silent_cmd(['openssl', 'x509', '-req', '-in', $reqfn, '-days', '3650',
- '-out', $pvessl_cert_fn, '-CAkey', $pveca_key_fn,
- '-CA', $pveca_cert_fn, '-CAserial', $pveca_srl_fn,
- '-extfile', $cfgfn]);
+ # wrap openssl with faketime to prevent bug #904
+ run_silent_cmd(['faketime', 'yesterday', 'openssl', 'x509', '-req',
+ '-in', $reqfn, '-days', '3650', '-out', $pvessl_cert_fn,
+ '-CAkey', $pveca_key_fn, '-CA', $pveca_cert_fn,
+ '-CAserial', $pveca_srl_fn, '-extfile', $cfgfn]);
};
if (my $err = $@) {
for my $line (@$data) {
my $entry = { 'time' => $start };
$start += $step;
- my $found_undefs;
for (my $i = 0; $i < $fields; $i++) {
my $name = $names->[$i];
if (defined(my $val = $line->[$i])) {
$entry->{$name} = $val;
} else {
- # we only add entryies with all data defined
- # extjs chart has problems with undefined values
- $found_undefs = 1;
+ # leave empty fields undefined
+ # maybe make this configurable?
}
}
- push @$res, $entry if !$found_undefs;
+ push @$res, $entry;
}
return $res;
"--width" => 800,
"--start" => - $reso*$count,
"--end" => 'now' ,
+ "--lower-limit" => 0,
);
my $socket = "/var/run/rrdcached.sock";
my $version;
my $infotag;
- if ($filename =~ m!^nodes/[^/]+/(openvz|qemu-server)/(\d+)\.conf$!) {
+ if ($filename =~ m!^nodes/[^/]+/(openvz|lxc|qemu-server)/(\d+)\.conf$!) {
my ($type, $vmid) = ($1, $2);
if ($vmlist && $vmlist->{ids} && $vmlist->{ids}->{$vmid}) {
$version = $vmlist->{ids}->{$vmid}->{version};
}
$infotag = "/$type/";
- } elsif ($filename =~ m!^nodes/[^/]+/lxc/(\d+)/config$!) {
- my $vmid = $1;
- if ($vmlist && $vmlist->{ids} && $vmlist->{ids}->{$vmid}) {
- $version = $vmlist->{ids}->{$vmid}->{version};
- }
- $infotag = "/lxc/";
} else {
$infotag = $filename;
$version = $versions->{$filename};
&$cfs_lock($lockid, $timeout, $code, @param);
}
+sub cfs_lock_domain {
+ my ($domainname, $timeout, $code, @param) = @_;
+
+ my $lockid = "domain-$domainname";
+
+ &$cfs_lock($lockid, $timeout, $code, @param);
+}
+
my $log_levels = {
"emerg" => 0,
"alert" => 1,
$msg = "empty message" if !$msg;
$ident = "" if !$ident;
- $ident = encode("ascii", decode_utf8($ident),
+ $ident = encode("ascii", $ident,
sub { sprintf "\\u%04x", shift });
- my $utf8 = decode_utf8($msg);
-
- my $ascii = encode("ascii", $utf8, sub { sprintf "\\u%04x", shift });
+ my $ascii = encode("ascii", $msg, sub { sprintf "\\u%04x", shift });
if ($ident) {
syslog($priority, "<%s> %s", $ident, $ascii);
return undef if $noerr;
- die "VM $vmid already exists\n" if $d->{type} eq 'qemu';
-
- die "CT $vmid already exists\n";
+ my $vmtypestr = $d->{type} eq 'qemu' ? 'VM' : 'CT';
+ die "$vmtypestr $vmid already exists on node '$d->{node}'\n";
}
sub check_node_exists {
}
}
+sub setup_sshd_config {
+
+ my $conf = PVE::Tools::file_get_contents($sshd_config_fn);
+
+ return if $conf =~ m/^PermitRootLogin\s+yes\s*$/m;
+
+ if ($conf !~ s/^#?PermitRootLogin.*$/PermitRootLogin yes/m) {
+ chomp $conf;
+ $conf .= "\nPermitRootLogin yes\n";
+ }
+
+ PVE::Tools::file_set_contents($sshd_config_fn, $conf);
+
+ PVE::Tools::run_command(['systemctl', 'reload-or-restart', 'sshd']);
+}
+
sub setup_rootsshconfig {
# create ssh key if it does not exist
}
my $hostkey = PVE::Tools::file_get_contents($ssh_host_rsa_id);
- die "can't parse $ssh_rsa_id" if $hostkey !~ m/^(ssh-rsa\s\S+)(\s.*)?$/;
+ # Note: file sometimes containe emty lines at start, so we use multiline match
+ die "can't parse $ssh_host_rsa_id" if $hostkey !~ m/^(ssh-rsa\s\S+)(\s.*)?$/m;
$hostkey = $1;
my $data = '';
format => 'email-opt',
description => "Specify email address to send notification from (default is root@\$hostname)",
},
+ max_workers => {
+ optional => 1,
+ type => 'integer',
+ minimum => 1,
+ description => "Defines how many workers (per node) are maximal started ".
+ " on actions like 'stopall VMs' or task from the ha-manager.",
+ },
+ fencing => {
+ optional => 1,
+ type => 'string',
+ default => 'watchdog',
+ enum => [ 'watchdog', 'hardware', 'both' ],
+ description => "Set the fencing mode of the HA cluster. Hardware mode " .
+ "needs a valid configuration of fence devices in /etc/pve/ha/fence.cfg." .
+ " With both all two modes are used. " .
+ " NOTE: 'hardware' and 'both' are EXPERIMENTAL & WIP",
+ },
},
};
sub parse_datacenter_config {
my ($filename, $raw) = @_;
- return PVE::JSONSchema::parse_config($datacenter_schema, $filename, $raw);
+ return PVE::JSONSchema::parse_config($datacenter_schema, $filename, $raw // '');
}
sub write_datacenter_config {
PVE::Cluster::cfs_register_file('corosync.conf.new', \&parse_corosync_conf,
\&write_corosync_conf);
+sub check_corosync_conf_exists {
+ my ($silent) = @_;
+
+ $silent = $silent // 0;
+
+ my $exists = -f "$basedir/corosync.conf";
+
+ warn "Corosync config '$basedir/corosync.conf' does not exist - is this node part of a cluster?\n"
+ if !$silent && !$exists;
+
+ return $exists;
+}
+
+# bash completion helpers
+
+sub complete_next_vmid {
+
+ my $vmlist = get_vmlist() || {};
+ my $idlist = $vmlist->{ids} || {};
+
+ for (my $i = 100; $i < 10000; $i++) {
+ return [$i] if !defined($idlist->{$i});
+ }
+
+ return [];
+}
+
+sub complete_vmid {
+
+ my $vmlist = get_vmlist();
+ my $ids = $vmlist->{ids} || {};
+
+ return [ keys %$ids ];
+}
+
+sub complete_local_vmid {
+
+ my $vmlist = get_vmlist();
+ my $ids = $vmlist->{ids} || {};
+
+ my $nodename = PVE::INotify::nodename();
+
+ my $res = [];
+ foreach my $vmid (keys %$ids) {
+ my $d = $ids->{$vmid};
+ next if !$d->{node} || $d->{node} ne $nodename;
+ push @$res, $vmid;
+ }
+
+ return $res;
+}
+
+sub complete_migration_target {
+
+ my $res = [];
+
+ my $nodename = PVE::INotify::nodename();
+
+ my $nodelist = get_nodelist();
+ foreach my $node (@$nodelist) {
+ next if $node eq $nodename;
+ push @$res, $node;
+ }
+
+ return $res;
+}
+
1;