package PVE::API2::Ceph::MDS;

use strict;
use warnings;

use PVE::Ceph::Tools;
use PVE::Ceph::Services;
use PVE::Cluster qw(cfs_read_file cfs_write_file);
use PVE::INotify;
use PVE::JSONSchema qw(get_standard_option);
use PVE::RADOS;
use PVE::RESTHandler;
use PVE::RPCEnvironment;

use base qw(PVE::RESTHandler);

__PACKAGE__->register_method ({
    name => 'index',
    path => '',
    method => 'GET',
    description => "MDS directory index.",
    permissions => {
	check => ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any => 1],
    },
    proxyto => 'node',
    protected => 1,
    parameters => {
	additionalProperties => 0,
	properties => {
	    node => get_standard_option('pve-node'),
	},
    },
    returns => {
	type => 'array',
	items => {
	    type => "object",
	    properties => {
		name => {
		    description => "The name (ID) for the MDS",
		},
		addr => {
		    type => 'string',
		    optional => 1,
		},
		host => {
		    type => 'string',
		    optional => 1,
		},
		state => {
		    type => 'string',
		    description => 'State of the MDS',
		},
		standby_replay => {
		    type => 'boolean',
		    optional => 1,
		    description => 'If true, the standby MDS is polling the active MDS for faster recovery (hot standby).',
		},
		rank => {
		    type => 'integer',
		    optional => 1,
		},
	    },
	},
	links => [ { rel => 'child', href => "{name}" } ],
    },
    code => sub {
	my ($param) = @_;

	PVE::Ceph::Tools::check_ceph_inited();

	my $res = [];

	my $cfg = cfs_read_file('ceph.conf');
	my $rados = PVE::RADOS->new();

	my $mds_hash = PVE::Ceph::Services::get_services_info("mds", $cfg, $rados);

	my $mds_state = PVE::Ceph::Services::get_cluster_mds_state($rados);
	foreach my $name (keys %$mds_state) {
	    my $d = $mds_state->{$name};
	    # just overwrite, this always provides more info
	    $mds_hash->{$name}->{$_} = $d->{$_} for keys %$d;
	}

	return PVE::RESTHandler::hash_to_array($mds_hash, 'name');
    }
});

__PACKAGE__->register_method ({
    name => 'createmds',
    path => '{name}',
    method => 'POST',
    description => "Create Ceph Metadata Server (MDS)",
    proxyto => 'node',
    protected => 1,
    permissions => {
	check => ['perm', '/', [ 'Sys.Modify' ]],
    },
    parameters => {
	additionalProperties => 0,
	properties => {
	    node => get_standard_option('pve-node'),
	    name => {
		type => 'string',
		optional => 1,
		default => 'nodename',
		pattern => PVE::Ceph::Services::SERVICE_REGEX,
		maxLength => 200,
		description => "The ID for the mds, when omitted the same as the nodename",
	    },
	    hotstandby => {
		type => 'boolean',
		optional => 1,
		default => '0',
		description => "Determines whether a ceph-mds daemon should poll and replay the log of an active MDS. ".
		    "Faster switch on MDS failure, but needs more idle resources.",
	    },
	},
    },
    returns => { type => 'string' },
    code => sub {
	my ($param) = @_;

	PVE::Ceph::Tools::check_ceph_installed('ceph_mds');

	PVE::Ceph::Tools::check_ceph_inited();

	my $rpcenv = PVE::RPCEnvironment::get();
	my $authuser = $rpcenv->get_user();

	my $nodename = $param->{node};
	$nodename = INotify::nodename() if $nodename eq 'localhost';

	my $mds_id = $param->{name} // $nodename;

	die "ID of the MDS cannot start with a number!\n" if ($mds_id =~ /^[0-9]/);

	my $worker = sub {
	    my $timeout = PVE::Ceph::Tools::get_config('long_rados_timeout');
	    my $rados = PVE::RADOS->new(timeout => $timeout);

	    my $cfg = cfs_read_file('ceph.conf');

	    my $section = "mds.$mds_id";

	    if (defined($cfg->{$section})) {
		die "MDS '$mds_id' already referenced in ceph config, abort!\n"
	    }

	    if (!defined($cfg->{mds}->{keyring})) {
		# $id isn't a perl variable but a ceph metavariable
		my $keyring = '/var/lib/ceph/mds/ceph-$id/keyring';

		$cfg->{mds}->{keyring} = $keyring;
	    }

	    $cfg->{$section}->{host} = $nodename;
	    $cfg->{$section}->{'mds_standby_for_name'} = 'pve';

	    if ($param->{hotstandby}) {
		$cfg->{$section}->{'mds_standby_replay'} = 'true';
	    }

	    cfs_write_file('ceph.conf', $cfg);

	    eval { PVE::Ceph::Services::create_mds($mds_id, $rados) };
	    if (my $err = $@) {
		# we abort early if the section is defined, so we know that we
		# wrote it at this point. Do not auto remove the service, could
		# do real harm for previously manual setup MDS
		warn "Encountered error, remove '$section' from ceph.conf\n";
		my $cfg = cfs_read_file('ceph.conf');
		delete $cfg->{$section};
		cfs_write_file('ceph.conf', $cfg);

		die "$err\n";
	    }
	};

	return $rpcenv->fork_worker('cephcreatemds', "mds.$mds_id", $authuser, $worker);
    }
});

__PACKAGE__->register_method ({
    name => 'destroymds',
    path => '{name}',
    method => 'DELETE',
    description => "Destroy Ceph Metadata Server",
    proxyto => 'node',
    protected => 1,
    permissions => {
	check => ['perm', '/', [ 'Sys.Modify' ]],
    },
    parameters => {
	additionalProperties => 0,
	properties => {
	    node => get_standard_option('pve-node'),
	    name => {
		description => 'The name (ID) of the mds',
		type => 'string',
		pattern => PVE::Ceph::Services::SERVICE_REGEX,
	    },
	},
    },
    returns => { type => 'string' },
    code => sub {
	my ($param) = @_;

	my $rpcenv = PVE::RPCEnvironment::get();

	my $authuser = $rpcenv->get_user();

	PVE::Ceph::Tools::check_ceph_inited();

	my $mds_id = $param->{name};

	my $worker = sub {
	    my $timeout = PVE::Ceph::Tools::get_config('long_rados_timeout');
	    my $rados = PVE::RADOS->new(timeout => $timeout);

	    my $cfg = cfs_read_file('ceph.conf');

	    if (defined($cfg->{"mds.$mds_id"})) {
		delete $cfg->{"mds.$mds_id"};
		cfs_write_file('ceph.conf', $cfg);
	    }

	    PVE::Ceph::Services::destroy_mds($mds_id, $rados);
	};

	return $rpcenv->fork_worker('cephdestroymds', "mds.$mds_id",  $authuser, $worker);
    }
});

1;
