1515// SPDX-License-Identifier: Apache-2.0
1616
1717use super :: defines:: { defaults, keys, values} ;
18- use super :: section:: { Section , SectionKind , Sections } ;
18+ use super :: section:: { names , Section , SectionKind , Sections } ;
1919use super :: yaml:: { Get , Yaml } ;
2020use crate :: config:: authentication:: Authentication ;
2121use crate :: config:: connection:: Connection ;
2222use crate :: config:: options:: Options ;
2323use crate :: config:: target:: TargetId ;
2424use crate :: ora_sql:: detect:: get_local_sid_names;
2525use crate :: types:: {
26- DescriptorSid , HostName , InstanceAlias , InstanceName , ServiceName , ServiceType , Sid ,
27- SqlBindParam ,
26+ DescriptorSid , HostName , InstanceAlias , InstanceName , SectionName , ServiceName , ServiceType ,
27+ Sid , SqlBindParam ,
2828} ;
2929use anyhow:: { anyhow, bail, Context , Result } ;
3030use std:: collections:: hash_map:: DefaultHasher ;
@@ -388,6 +388,7 @@ pub struct CustomInstance {
388388 target_id : Option < TargetId > ,
389389 alias : Option < InstanceAlias > ,
390390 piggyback : Option < Piggyback > ,
391+ custom_metrics : Vec < Section > ,
391392}
392393
393394impl CustomInstance {
@@ -404,6 +405,7 @@ impl CustomInstance {
404405 target_id,
405406 alias,
406407 piggyback,
408+ custom_metrics : vec ! [ ] ,
407409 }
408410 }
409411
@@ -413,13 +415,23 @@ impl CustomInstance {
413415 main_conn : & Connection ,
414416 sections : & Sections ,
415417 ) -> Result < Self > {
416- Ok ( Self :: new (
417- ensure_auth ( yaml, main_auth) ?,
418- ensure_conn ( yaml, main_conn) ?,
419- TargetId :: from_yaml ( yaml) ?,
420- yaml. get_string ( keys:: ALIAS ) . map ( InstanceAlias :: from) ,
421- Piggyback :: from_yaml ( yaml, sections) ?,
422- ) )
418+ Ok ( Self {
419+ auth : ensure_auth ( yaml, main_auth) ?,
420+ conn : ensure_conn ( yaml, main_conn) ?,
421+ target_id : TargetId :: from_yaml ( yaml) ?,
422+ alias : yaml. get_string ( keys:: ALIAS ) . map ( InstanceAlias :: from) ,
423+ piggyback : Piggyback :: from_yaml ( yaml, sections) ?,
424+ custom_metrics : Sections :: get_sections (
425+ yaml. get ( keys:: CUSTOM_METRICS ) ,
426+ Some ( defaults:: CUSTOM_METRIC_SEPARATOR ) ,
427+ Some ( & SectionName :: from ( names:: CUSTOM_METRIC . to_string ( ) ) ) ,
428+ )
429+ . unwrap_or_default ( ) ,
430+ } )
431+ }
432+
433+ pub fn custom_metrics ( & self ) -> & [ Section ] {
434+ & self . custom_metrics
423435 }
424436
425437 /// may be overridden with a connection value
@@ -547,10 +559,9 @@ oracle:
547559 timeout: 11 # optional, default 5
548560 tns_admin: "/path/to/oracle/config/files/" # optional, default: agent plugin config folder. Points to the location of sqlnet.ora and tnsnames.ora
549561 oracle_local_registry: "/etc/oracle/olr.loc" # optional, default: folder of oracle configuration files like oratab
550- custom_metrics: # additional queries which generates <<<oracle_sql>>>> + item [SID|name] for each instance
551- - my_custom_metric: # for item generation, mandatory
552- sql: "select * from dual" # optional
553- is_async: no # optional, default: no
562+ custom_metrics: # additional queries that produce <<<oracle_sql:sep(58)>>> + [[[SID|item_name]]] for each instance
563+ - my_custom_metric: # item name (mandatory); becomes the subsection item
564+ sql: "select 'details:hello' from dual" # inline SQL executed against the instance
554565 sections: # optional
555566 - instance: # special section
556567 affinity: "all" # optional, default: "db", values: "all", "db", "asm"
@@ -758,6 +769,8 @@ piggyback:
758769 custom. item_value( ) . map( |v| v. as_str( ) ) ,
759770 Some ( "my_custom_metric" )
760771 ) ;
772+ assert_eq ! ( custom. sql( ) , Some ( "select 'details:hello' from dual" ) ) ;
773+ assert_eq ! ( custom. sep( ) , defaults:: CUSTOM_METRIC_SEPARATOR ) ;
761774
762775 product. sections ( ) . iter ( ) . for_each ( |s| {
763776 if s. name ( ) . as_str ( ) == names:: CUSTOM_METRIC {
@@ -773,6 +786,77 @@ piggyback:
773786 assert_eq ! ( c. mode, Mode :: Special ) ;
774787 }
775788
789+ #[ test]
790+ fn test_per_instance_custom_metrics_parsed_and_override_globals ( ) {
791+ const YAML : & str = r#"
792+ oracle:
793+ main:
794+ authentication:
795+ username: u
796+ password: p
797+ type: standard
798+ connection:
799+ hostname: localhost
800+ custom_metrics:
801+ - shared_metric:
802+ sql: "select 'details:GLOBAL' from dual"
803+ - global_only:
804+ sql: "select 'details:global_only' from dual"
805+ instances:
806+ - service_name: INST_A
807+ custom_metrics:
808+ - shared_metric:
809+ sql: "select 'details:PER_INSTANCE' from dual"
810+ - instance_only:
811+ sql: "select 'details:instance_only' from dual"
812+ - service_name: INST_B
813+ "# ;
814+ let c = Config :: from_string ( YAML ) . unwrap ( ) . unwrap ( ) ;
815+ // Global custom_metrics live on Sections — both global entries present.
816+ let global_custom: Vec < _ > = c
817+ . product ( )
818+ . sections ( )
819+ . iter ( )
820+ . filter_map ( |s| s. item_value ( ) . map ( |v| ( v. as_str ( ) , s. sql ( ) ) ) )
821+ . collect ( ) ;
822+ assert ! (
823+ global_custom. contains( & ( "shared_metric" , Some ( "select 'details:GLOBAL' from dual" ) ) ) ,
824+ "global custom_metrics: {global_custom:?}"
825+ ) ;
826+ assert ! (
827+ global_custom. contains( & (
828+ "global_only" ,
829+ Some ( "select 'details:global_only' from dual" )
830+ ) ) ,
831+ "global custom_metrics: {global_custom:?}"
832+ ) ;
833+
834+ let instances = c. instances ( ) ;
835+ let inst_a = & instances[ 0 ] ;
836+ let inst_a_metrics: Vec < _ > = inst_a
837+ . custom_metrics ( )
838+ . iter ( )
839+ . map ( |s| ( s. item_value ( ) . unwrap ( ) . as_str ( ) , s. sql ( ) ) )
840+ . collect ( ) ;
841+ // Per-instance: same shared_metric name, but per-instance SQL.
842+ assert ! (
843+ inst_a_metrics. contains( & (
844+ "shared_metric" ,
845+ Some ( "select 'details:PER_INSTANCE' from dual" )
846+ ) ) ,
847+ "per-instance metrics for INST_A: {inst_a_metrics:?}"
848+ ) ;
849+ assert ! (
850+ inst_a_metrics. contains( & (
851+ "instance_only" ,
852+ Some ( "select 'details:instance_only' from dual" )
853+ ) ) ,
854+ "per-instance metrics for INST_A: {inst_a_metrics:?}"
855+ ) ;
856+ // INST_B has no per-instance custom_metrics.
857+ assert ! ( instances[ 1 ] . custom_metrics( ) . is_empty( ) ) ;
858+ }
859+
776860 #[ test]
777861 fn test_config_connection ( ) {
778862 let c = Config :: from_string ( data:: TEST_CONFIG ) . unwrap ( ) . unwrap ( ) ;
0 commit comments