1 package PVE
::API2
::Cluster
::Notifications
;
6 use Storable
qw(dclone);
9 use PVE
::Exception
qw(raise_param_exc);
10 use PVE
::Tools
qw(extract_param);
11 use PVE
::JSONSchema
qw(get_standard_option);
15 use base
qw(PVE::RESTHandler);
17 sub make_properties_optional
{
18 my ($properties) = @_;
19 $properties = dclone
($properties);
21 for my $key (keys %$properties) {
22 $properties->{$key}->{optional
} = 1 if $key ne 'name';
28 sub remove_protected_properties
{
29 my ($properties, $to_remove) = @_;
30 $properties = dclone
($properties);
32 for my $key (keys %$properties) {
33 if (grep /^$key$/, @$to_remove) {
34 delete $properties->{$key};
44 if (!(ref($api_error) eq 'HASH' && $api_error->{message
} && $api_error->{code
})) {
48 my $msg = "$api_error->{message}\n";
49 my $exc = PVE
::Exception-
>new($msg, code
=> $api_error->{code
});
51 my (undef, $filename, $line) = caller;
53 $exc->{filename
} = $filename;
59 sub filter_entities_by_privs
{
60 my ($rpcenv, $entities) = @_;
61 my $authuser = $rpcenv->get_user();
63 my $can_see_mapping_privs = ['Mapping.Modify', 'Mapping.Use', 'Mapping.Audit'];
65 my $filtered = [grep {
68 "/mapping/notification/$_->{name}",
69 $can_see_mapping_privs,
82 # Check keys in datacenter.cfg
83 my $dc_conf = PVE
::Cluster
::cfs_read_file
('datacenter.cfg');
84 for my $key (qw(target-package-updates target-replication target-fencing)) {
85 if ($dc_conf->{notify
} && $dc_conf->{notify
}->{$key} eq $target) {
91 my $jobs_conf = PVE
::Cluster
::cfs_read_file
('jobs.cfg');
92 for my $key (keys %{$jobs_conf->{ids
}}) {
93 my $job = $jobs_conf->{ids
}->{$key};
94 if ($job->{'notification-target'} eq $target) {
99 return join(', ', @$used_by);
102 __PACKAGE__-
>register_method ({
106 description
=> 'Index for notification-related API endpoints.',
107 permissions
=> { user
=> 'all' },
109 additionalProperties
=> 0,
118 links
=> [ { rel
=> 'child', href
=> '{name}' } ],
122 { name
=> 'endpoints' },
123 { name
=> 'filters' },
124 { name
=> 'groups' },
125 { name
=> 'targets' },
132 __PACKAGE__-
>register_method ({
133 name
=> 'endpoints_index',
136 description
=> 'Index for all available endpoint types.',
137 permissions
=> { user
=> 'all' },
139 additionalProperties
=> 0,
148 links
=> [ { rel
=> 'child', href
=> '{name}' } ],
152 { name
=> 'gotify' },
153 { name
=> 'sendmail' },
160 __PACKAGE__-
>register_method ({
161 name
=> 'get_all_targets',
164 description
=> 'Returns a list of all entities that can be used as notification targets' .
165 ' (endpoints and groups).',
167 description
=> "Only lists entries where you have 'Mapping.Modify', 'Mapping.Use' or"
168 . " 'Mapping.Audit' permissions on '/mapping/notification/<name>'.",
173 additionalProperties
=> 0,
182 description
=> 'Name of the endpoint/group.',
184 format
=> 'pve-configid',
187 description
=> 'Type of the endpoint or group.',
189 enum
=> [qw(sendmail gotify group)],
192 description
=> 'Comment',
198 links
=> [ { rel
=> 'child', href
=> '{name}' } ],
201 my $config = PVE
::Notify
::read_config
();
202 my $rpcenv = PVE
::RPCEnvironment
::get
();
207 for my $target (@{$config->get_sendmail_endpoints()}) {
209 name
=> $target->{name
},
210 comment
=> $target->{comment
},
215 for my $target (@{$config->get_gotify_endpoints()}) {
217 name
=> $target->{name
},
218 comment
=> $target->{comment
},
223 for my $target (@{$config->get_groups()}) {
225 name
=> $target->{name
},
226 comment
=> $target->{comment
},
234 raise_api_error
($@) if $@;
236 return filter_entities_by_privs
($rpcenv, $targets);
240 __PACKAGE__-
>register_method ({
241 name
=> 'test_target',
242 path
=> 'targets/{name}/test',
245 description
=> 'Send a test notification to a provided target.',
248 ['perm', '/mapping/notification/{name}', ['Mapping.Use']],
249 ['perm', '/mapping/notification/{name}', ['Mapping.Modify']],
250 ['perm', '/mapping/notification/{name}', ['Mapping.Audit']],
254 additionalProperties
=> 0,
257 description
=> 'Name of the target.',
259 format
=> 'pve-configid'
263 returns
=> { type
=> 'null' },
266 my $name = extract_param
($param, 'name');
268 my $config = PVE
::Notify
::read_config
();
271 $config->test_target($name);
274 raise_api_error
($@) if $@;
280 my $group_properties = {
282 description
=> 'Name of the group.',
284 format
=> 'pve-configid',
290 format
=> 'pve-configid',
292 description
=> 'List of included endpoints',
295 description
=> 'Comment',
300 description
=> 'Name of the filter that should be applied.',
302 format
=> 'pve-configid',
307 __PACKAGE__-
>register_method ({
308 name
=> 'get_groups',
311 description
=> 'Returns a list of all groups',
314 description
=> "Only lists entries where you have 'Mapping.Modify', 'Mapping.Use' or"
315 . " 'Mapping.Audit' permissions on '/mapping/notification/<name>'.",
319 additionalProperties
=> 0,
326 properties
=> $group_properties,
328 links
=> [ { rel
=> 'child', href
=> '{name}' } ],
331 my $config = PVE
::Notify
::read_config
();
332 my $rpcenv = PVE
::RPCEnvironment
::get
();
334 my $entities = eval {
335 $config->get_groups();
337 raise_api_error
($@) if $@;
339 return filter_entities_by_privs
($rpcenv, $entities);
343 __PACKAGE__-
>register_method ({
345 path
=> 'groups/{name}',
347 description
=> 'Return a specific group',
351 ['perm', '/mapping/notification/{name}', ['Mapping.Modify']],
352 ['perm', '/mapping/notification/{name}', ['Mapping.Audit']],
356 additionalProperties
=> 0,
360 format
=> 'pve-configid',
368 digest
=> get_standard_option
('pve-config-digest'),
373 my $name = extract_param
($param, 'name');
375 my $config = PVE
::Notify
::read_config
();
378 $config->get_group($name)
381 raise_api_error
($@) if $@;
382 $group->{digest
} = $config->digest();
388 __PACKAGE__-
>register_method ({
389 name
=> 'create_group',
393 description
=> 'Create a new group',
395 check
=> ['perm', '/mapping/notification', ['Mapping.Modify']],
398 additionalProperties
=> 0,
399 properties
=> $group_properties,
401 returns
=> { type
=> 'null' },
405 my $name = extract_param
($param, 'name');
406 my $endpoint = extract_param
($param, 'endpoint');
407 my $comment = extract_param
($param, 'comment');
408 my $filter = extract_param
($param, 'filter');
411 PVE
::Notify
::lock_config
(sub {
412 my $config = PVE
::Notify
::read_config
();
421 PVE
::Notify
::write_config
($config);
425 raise_api_error
($@) if $@;
430 __PACKAGE__-
>register_method ({
431 name
=> 'update_group',
432 path
=> 'groups/{name}',
435 description
=> 'Update existing group',
437 check
=> ['perm', '/mapping/notification/{name}', ['Mapping.Modify']],
440 additionalProperties
=> 0,
442 %{ make_properties_optional
($group_properties) },
447 format
=> 'pve-configid',
450 description
=> 'A list of settings you want to delete.',
452 digest
=> get_standard_option
('pve-config-digest'),
455 returns
=> { type
=> 'null' },
459 my $name = extract_param
($param, 'name');
460 my $endpoint = extract_param
($param, 'endpoint');
461 my $comment = extract_param
($param, 'comment');
462 my $filter = extract_param
($param, 'filter');
463 my $digest = extract_param
($param, 'digest');
464 my $delete = extract_param
($param, 'delete');
467 PVE
::Notify
::lock_config
(sub {
468 my $config = PVE
::Notify
::read_config
();
470 $config->update_group(
479 PVE
::Notify
::write_config
($config);
483 raise_api_error
($@) if $@;
488 __PACKAGE__-
>register_method ({
489 name
=> 'delete_group',
491 path
=> 'groups/{name}',
493 description
=> 'Remove group',
495 check
=> ['perm', '/mapping/notification/{name}', ['Mapping.Modify']],
498 additionalProperties
=> 0,
502 format
=> 'pve-configid',
506 returns
=> { type
=> 'null' },
509 my $name = extract_param
($param, 'name');
511 my $used_by = target_used_by
($name);
513 raise_param_exc
({'name' => "Cannot remove $name, used by: $used_by"});
517 PVE
::Notify
::lock_config
(sub {
518 my $config = PVE
::Notify
::read_config
();
519 $config->delete_group($name);
520 PVE
::Notify
::write_config
($config);
524 raise_api_error
($@) if $@;
529 my $sendmail_properties = {
531 description
=> 'The name of the endpoint.',
533 format
=> 'pve-configid',
539 format
=> 'email-or-username',
541 description
=> 'List of email recipients',
548 format
=> 'pve-userid',
550 description
=> 'List of users',
554 description
=> '`From` address for the mail',
559 description
=> 'Author of the mail',
564 description
=> 'Comment',
569 description
=> 'Name of the filter that should be applied.',
571 format
=> 'pve-configid',
576 __PACKAGE__-
>register_method ({
577 name
=> 'get_sendmail_endpoints',
578 path
=> 'endpoints/sendmail',
580 description
=> 'Returns a list of all sendmail endpoints',
582 description
=> "Only lists entries where you have 'Mapping.Modify', 'Mapping.Use' or"
583 . " 'Mapping.Audit' permissions on '/mapping/notification/<name>'.",
588 additionalProperties
=> 0,
595 properties
=> $sendmail_properties,
597 links
=> [ { rel
=> 'child', href
=> '{name}' } ],
600 my $config = PVE
::Notify
::read_config
();
601 my $rpcenv = PVE
::RPCEnvironment
::get
();
603 my $entities = eval {
604 $config->get_sendmail_endpoints();
606 raise_api_error
($@) if $@;
608 return filter_entities_by_privs
($rpcenv, $entities);
612 __PACKAGE__-
>register_method ({
613 name
=> 'get_sendmail_endpoint',
614 path
=> 'endpoints/sendmail/{name}',
616 description
=> 'Return a specific sendmail endpoint',
619 ['perm', '/mapping/notification/{name}', ['Mapping.Modify']],
620 ['perm', '/mapping/notification/{name}', ['Mapping.Audit']],
625 additionalProperties
=> 0,
629 format
=> 'pve-configid',
636 %$sendmail_properties,
637 digest
=> get_standard_option
('pve-config-digest'),
643 my $name = extract_param
($param, 'name');
645 my $config = PVE
::Notify
::read_config
();
646 my $endpoint = eval {
647 $config->get_sendmail_endpoint($name)
650 raise_api_error
($@) if $@;
651 $endpoint->{digest
} = $config->digest();
657 __PACKAGE__-
>register_method ({
658 name
=> 'create_sendmail_endpoint',
659 path
=> 'endpoints/sendmail',
662 description
=> 'Create a new sendmail endpoint',
664 check
=> ['perm', '/mapping/notification', ['Mapping.Modify']],
667 additionalProperties
=> 0,
668 properties
=> $sendmail_properties,
670 returns
=> { type
=> 'null' },
674 my $name = extract_param
($param, 'name');
675 my $mailto = extract_param
($param, 'mailto');
676 my $mailto_user = extract_param
($param, 'mailto-user');
677 my $from_address = extract_param
($param, 'from-address');
678 my $author = extract_param
($param, 'author');
679 my $comment = extract_param
($param, 'comment');
680 my $filter = extract_param
($param, 'filter');
683 PVE
::Notify
::lock_config
(sub {
684 my $config = PVE
::Notify
::read_config
();
686 $config->add_sendmail_endpoint(
696 PVE
::Notify
::write_config
($config);
700 raise_api_error
($@) if $@;
705 __PACKAGE__-
>register_method ({
706 name
=> 'update_sendmail_endpoint',
707 path
=> 'endpoints/sendmail/{name}',
710 description
=> 'Update existing sendmail endpoint',
712 check
=> ['perm', '/mapping/notification/{name}', ['Mapping.Modify']],
715 additionalProperties
=> 0,
717 %{ make_properties_optional
($sendmail_properties) },
722 format
=> 'pve-configid',
725 description
=> 'A list of settings you want to delete.',
727 digest
=> get_standard_option
('pve-config-digest'),
731 returns
=> { type
=> 'null' },
735 my $name = extract_param
($param, 'name');
736 my $mailto = extract_param
($param, 'mailto');
737 my $mailto_user = extract_param
($param, 'mailto-user');
738 my $from_address = extract_param
($param, 'from-address');
739 my $author = extract_param
($param, 'author');
740 my $comment = extract_param
($param, 'comment');
741 my $filter = extract_param
($param, 'filter');
743 my $delete = extract_param
($param, 'delete');
744 my $digest = extract_param
($param, 'digest');
747 PVE
::Notify
::lock_config
(sub {
748 my $config = PVE
::Notify
::read_config
();
750 $config->update_sendmail_endpoint(
762 PVE
::Notify
::write_config
($config);
766 raise_api_error
($@) if $@;
771 __PACKAGE__-
>register_method ({
772 name
=> 'delete_sendmail_endpoint',
774 path
=> 'endpoints/sendmail/{name}',
776 description
=> 'Remove sendmail endpoint',
778 check
=> ['perm', '/mapping/notification', ['Mapping.Modify']],
781 additionalProperties
=> 0,
785 format
=> 'pve-configid',
789 returns
=> { type
=> 'null' },
792 my $name = extract_param
($param, 'name');
794 my $used_by = target_used_by
($name);
796 raise_param_exc
({'name' => "Cannot remove $name, used by: $used_by"});
800 PVE
::Notify
::lock_config
(sub {
801 my $config = PVE
::Notify
::read_config
();
802 $config->delete_sendmail_endpoint($name);
803 PVE
::Notify
::write_config
($config);
807 raise_api_error
($@) if ($@);
812 my $gotify_properties = {
814 description
=> 'The name of the endpoint.',
816 format
=> 'pve-configid',
819 description
=> 'Server URL',
823 description
=> 'Secret token',
827 description
=> 'Comment',
832 description
=> 'Name of the filter that should be applied.',
834 format
=> 'pve-configid',
839 __PACKAGE__-
>register_method ({
840 name
=> 'get_gotify_endpoints',
841 path
=> 'endpoints/gotify',
843 description
=> 'Returns a list of all gotify endpoints',
846 description
=> "Only lists entries where you have 'Mapping.Modify', 'Mapping.Use' or"
847 . " 'Mapping.Audit' permissions on '/mapping/notification/<name>'.",
851 additionalProperties
=> 0,
858 properties
=> remove_protected_properties
($gotify_properties, ['token']),
860 links
=> [ { rel
=> 'child', href
=> '{name}' } ],
863 my $config = PVE
::Notify
::read_config
();
864 my $rpcenv = PVE
::RPCEnvironment
::get
();
866 my $entities = eval {
867 $config->get_gotify_endpoints();
869 raise_api_error
($@) if $@;
871 return filter_entities_by_privs
($rpcenv, $entities);
875 __PACKAGE__-
>register_method ({
876 name
=> 'get_gotify_endpoint',
877 path
=> 'endpoints/gotify/{name}',
879 description
=> 'Return a specific gotify endpoint',
883 ['perm', '/mapping/notification/{name}', ['Mapping.Modify']],
884 ['perm', '/mapping/notification/{name}', ['Mapping.Audit']],
888 additionalProperties
=> 0,
892 format
=> 'pve-configid',
893 description
=> 'Name of the endpoint.'
900 %{ remove_protected_properties
($gotify_properties, ['token']) },
901 digest
=> get_standard_option
('pve-config-digest'),
906 my $name = extract_param
($param, 'name');
908 my $config = PVE
::Notify
::read_config
();
909 my $endpoint = eval {
910 $config->get_gotify_endpoint($name)
913 raise_api_error
($@) if $@;
914 $endpoint->{digest
} = $config->digest();
920 __PACKAGE__-
>register_method ({
921 name
=> 'create_gotify_endpoint',
922 path
=> 'endpoints/gotify',
925 description
=> 'Create a new gotify endpoint',
927 check
=> ['perm', '/mapping/notification', ['Mapping.Modify']],
930 additionalProperties
=> 0,
931 properties
=> $gotify_properties,
933 returns
=> { type
=> 'null' },
937 my $name = extract_param
($param, 'name');
938 my $server = extract_param
($param, 'server');
939 my $token = extract_param
($param, 'token');
940 my $comment = extract_param
($param, 'comment');
941 my $filter = extract_param
($param, 'filter');
944 PVE
::Notify
::lock_config
(sub {
945 my $config = PVE
::Notify
::read_config
();
947 $config->add_gotify_endpoint(
955 PVE
::Notify
::write_config
($config);
959 raise_api_error
($@) if $@;
964 __PACKAGE__-
>register_method ({
965 name
=> 'update_gotify_endpoint',
966 path
=> 'endpoints/gotify/{name}',
969 description
=> 'Update existing gotify endpoint',
971 check
=> ['perm', '/mapping/notification/{name}', ['Mapping.Modify']],
974 additionalProperties
=> 0,
976 %{ make_properties_optional
($gotify_properties) },
981 format
=> 'pve-configid',
984 description
=> 'A list of settings you want to delete.',
986 digest
=> get_standard_option
('pve-config-digest'),
989 returns
=> { type
=> 'null' },
993 my $name = extract_param
($param, 'name');
994 my $server = extract_param
($param, 'server');
995 my $token = extract_param
($param, 'token');
996 my $comment = extract_param
($param, 'comment');
997 my $filter = extract_param
($param, 'filter');
999 my $delete = extract_param
($param, 'delete');
1000 my $digest = extract_param
($param, 'digest');
1003 PVE
::Notify
::lock_config
(sub {
1004 my $config = PVE
::Notify
::read_config
();
1006 $config->update_gotify_endpoint(
1016 PVE
::Notify
::write_config
($config);
1020 raise_api_error
($@) if $@;
1025 __PACKAGE__-
>register_method ({
1026 name
=> 'delete_gotify_endpoint',
1028 path
=> 'endpoints/gotify/{name}',
1030 description
=> 'Remove gotify endpoint',
1032 check
=> ['perm', '/mapping/notification/{name}', ['Mapping.Modify']],
1035 additionalProperties
=> 0,
1039 format
=> 'pve-configid',
1043 returns
=> { type
=> 'null' },
1046 my $name = extract_param
($param, 'name');
1048 my $used_by = target_used_by
($name);
1050 raise_param_exc
({'name' => "Cannot remove $name, used by: $used_by"});
1054 PVE
::Notify
::lock_config
(sub {
1055 my $config = PVE
::Notify
::read_config
();
1056 $config->delete_gotify_endpoint($name);
1057 PVE
::Notify
::write_config
($config);
1061 raise_api_error
($@) if $@;
1066 my $filter_properties = {
1068 description
=> 'Name of the endpoint.',
1070 format
=> 'pve-configid',
1074 description
=> 'Minimum severity to match',
1076 enum
=> [qw(info notice warning error)],
1080 description
=> "Choose between 'and' and 'or' for when multiple properties are specified",
1082 enum
=> [qw(and or)],
1087 description
=> 'Invert match of the whole filter',
1091 description
=> 'Comment',
1097 __PACKAGE__-
>register_method ({
1098 name
=> 'get_filters',
1101 description
=> 'Returns a list of all filters',
1104 description
=> "Only lists entries where you have 'Mapping.Modify', 'Mapping.Use' or"
1105 . " 'Mapping.Audit' permissions on '/mapping/notification/<name>'.",
1109 additionalProperties
=> 0,
1116 properties
=> $filter_properties,
1118 links
=> [ { rel
=> 'child', href
=> '{name}' } ],
1121 my $config = PVE
::Notify
::read_config
();
1122 my $rpcenv = PVE
::RPCEnvironment
::get
();
1124 my $entities = eval {
1125 $config->get_filters();
1127 raise_api_error
($@) if $@;
1129 return filter_entities_by_privs
($rpcenv, $entities);
1133 __PACKAGE__-
>register_method ({
1134 name
=> 'get_filter',
1135 path
=> 'filters/{name}',
1137 description
=> 'Return a specific filter',
1141 ['perm', '/mapping/notification/{name}', ['Mapping.Modify']],
1142 ['perm', '/mapping/notification/{name}', ['Mapping.Audit']],
1146 additionalProperties
=> 0,
1150 format
=> 'pve-configid',
1157 %$filter_properties,
1158 digest
=> get_standard_option
('pve-config-digest'),
1163 my $name = extract_param
($param, 'name');
1165 my $config = PVE
::Notify
::read_config
();
1168 $config->get_filter($name)
1171 raise_api_error
($@) if $@;
1172 $filter->{digest
} = $config->digest();
1178 __PACKAGE__-
>register_method ({
1179 name
=> 'create_filter',
1183 description
=> 'Create a new filter',
1186 check
=> ['perm', '/mapping/notification', ['Mapping.Modify']],
1189 additionalProperties
=> 0,
1190 properties
=> $filter_properties,
1192 returns
=> { type
=> 'null' },
1196 my $name = extract_param
($param, 'name');
1197 my $min_severity = extract_param
($param, 'min-severity');
1198 my $mode = extract_param
($param, 'mode');
1199 my $invert_match = extract_param
($param, 'invert-match');
1200 my $comment = extract_param
($param, 'comment');
1203 PVE
::Notify
::lock_config
(sub {
1204 my $config = PVE
::Notify
::read_config
();
1206 $config->add_filter(
1214 PVE
::Notify
::write_config
($config);
1218 raise_api_error
($@) if $@;
1223 __PACKAGE__-
>register_method ({
1224 name
=> 'update_filter',
1225 path
=> 'filters/{name}',
1228 description
=> 'Update existing filter',
1230 check
=> ['perm', '/mapping/notification/{name}', ['Mapping.Modify']],
1233 additionalProperties
=> 0,
1235 %{ make_properties_optional
($filter_properties) },
1240 format
=> 'pve-configid',
1243 description
=> 'A list of settings you want to delete.',
1245 digest
=> get_standard_option
('pve-config-digest'),
1248 returns
=> { type
=> 'null' },
1252 my $name = extract_param
($param, 'name');
1253 my $min_severity = extract_param
($param, 'min-severity');
1254 my $mode = extract_param
($param, 'mode');
1255 my $invert_match = extract_param
($param, 'invert-match');
1256 my $comment = extract_param
($param, 'comment');
1257 my $digest = extract_param
($param, 'digest');
1258 my $delete = extract_param
($param, 'delete');
1261 PVE
::Notify
::lock_config
(sub {
1262 my $config = PVE
::Notify
::read_config
();
1264 $config->update_filter(
1274 PVE
::Notify
::write_config
($config);
1278 raise_api_error
($@) if $@;
1283 __PACKAGE__-
>register_method ({
1284 name
=> 'delete_filter',
1286 path
=> 'filters/{name}',
1288 description
=> 'Remove filter',
1290 check
=> ['perm', '/mapping/notification/{name}', ['Mapping.Modify']],
1293 additionalProperties
=> 0,
1297 format
=> 'pve-configid',
1301 returns
=> { type
=> 'null' },
1304 my $name = extract_param
($param, 'name');
1307 PVE
::Notify
::lock_config
(sub {
1308 my $config = PVE
::Notify
::read_config
();
1309 $config->delete_filter($name);
1310 PVE
::Notify
::write_config
($config);
1314 raise_api_error
($@) if $@;