55#include " space_monitor.h"
66#include " mon_main.h"
77#include " s3.h"
8+ #include " types.h"
9+ #include < ydb/public/api/protos/ydb_export.pb.h>
10+ #include < ydb/core/protos/s3_settings.pb.h>
11+ #include < ydb/core/protos/blob_depot_config.pb.h>
812
913namespace NKikimr ::NBlobDepot {
1014
@@ -85,6 +89,7 @@ namespace NKikimr::NBlobDepot {
8589 }
8690
8791 HTML (Stream) {
92+ Stream << " <a href='app?TabletID=" << Self->TabletID () << " '>Back to main page</a>" ;
8893 DIV_CLASS (" panel panel-info" ) {
8994 DIV_CLASS (" panel-heading" ) {
9095 Stream << " Data" ;
@@ -380,6 +385,153 @@ namespace NKikimr::NBlobDepot {
380385 }
381386 };
382387
388+ class TBlobDepot ::TTxMonS3Config : public NTabletFlatExecutor::TTransactionBase<TBlobDepot> {
389+ std::unique_ptr<NMon::TEvRemoteHttpInfo::THandle> Request;
390+ TStringStream Stream;
391+
392+ public:
393+ TTxType GetTxType () const override { return NKikimrBlobDepot::TXTYPE_MON_DATA; }
394+
395+ TTxMonS3Config (TBlobDepot *self, NMon::TEvRemoteHttpInfo::TPtr ev)
396+ : TTransactionBase(self)
397+ , Request(ev.Release())
398+ {}
399+
400+ bool Execute (TTransactionContext& /* txc*/ , const TActorContext&) override {
401+ HTML (Stream) {
402+ Stream << " <a href='app?TabletID=" << Self->TabletID () << " '>Back to main page</a>" ;
403+ DIV_CLASS (" panel panel-info" ) {
404+ DIV_CLASS (" panel-heading" ) {
405+ Stream << " S3 Configuration" ;
406+ }
407+ DIV_CLASS (" panel-body" ) {
408+ TABLE_CLASS (" table" ) {
409+ TABLEHEAD () {
410+ TABLER () {
411+ TABLEH () { Stream << " Setting" ; }
412+ TABLEH () { Stream << " Value" ; }
413+ }
414+ }
415+ TABLEBODY () {
416+ // Check if S3 is configured
417+ if (!Self->Config .HasS3BackendSettings ()) {
418+ TABLER () {
419+ TABLED () { Stream << " Status" ; }
420+ TABLED () { Stream << " S3 not configured" ; }
421+ }
422+ return true ;
423+ }
424+
425+ const auto & s3BackendSettings = Self->Config .GetS3BackendSettings ();
426+
427+ #define BD_MON_TABLE_ROW (key, value ) \
428+ do { \
429+ TABLER () { \
430+ TABLED () { Stream << key ;} \
431+ TABLED () { Stream << value ;} \
432+ } \
433+ } while (false )
434+
435+ #define BD_MON_TABLE_ROW_WITH_FORMATTER (config, key, formatter ) \
436+ do { \
437+ if (config.Has ##key ()) { \
438+ BD_MON_TABLE_ROW (#key, formatter (config.Get ##key ())); \
439+ } \
440+ } while (false )
441+
442+
443+ if (!s3BackendSettings.HasSettings ()) {
444+ BD_MON_TABLE_ROW (" Status" , " S3 settings not configured" );
445+ return true ;
446+ }
447+
448+ const auto & s3Settings = s3BackendSettings.GetSettings ();
449+
450+ TString mode;
451+ if (s3BackendSettings.HasAsyncMode ()) {
452+ mode = " Async" ;
453+ } else if (s3BackendSettings.HasSyncMode ()) {
454+ mode = " Sync" ;
455+ } else {
456+ mode = " Unknown" ;
457+ }
458+ BD_MON_TABLE_ROW (" Mode" , mode);
459+
460+ auto id_formatter = [] (auto x) { return x; };
461+
462+ // Display async mode settings if applicable
463+ if (s3BackendSettings.HasAsyncMode ()) {
464+ const auto & asyncMode = s3BackendSettings.GetAsyncMode ();
465+
466+ auto formatBytePerSecond = [](ui64 bytes) -> TString {
467+ return FormatByteSize (bytes) + " /s" ;
468+ };
469+
470+ BD_MON_TABLE_ROW_WITH_FORMATTER (asyncMode, MaxPendingBytes, FormatByteSize);
471+ BD_MON_TABLE_ROW_WITH_FORMATTER (asyncMode, ThrottleStartBytes, FormatByteSize);
472+ BD_MON_TABLE_ROW_WITH_FORMATTER (asyncMode, ThrottleMaxBytesPerSecond, formatBytePerSecond);
473+ BD_MON_TABLE_ROW_WITH_FORMATTER (asyncMode, UploadPutsInFlight, id_formatter);
474+
475+ }
476+
477+ BD_MON_TABLE_ROW (" Endpoint" , s3Settings.GetEndpoint ());
478+
479+ TString schemeStr;
480+ if (s3Settings.HasScheme ()) {
481+ switch (s3Settings.GetScheme ()) {
482+ case NKikimrSchemeOp::TS3Settings::HTTPS:
483+ schemeStr = " https" ;
484+ break ;
485+ case NKikimrSchemeOp::TS3Settings::HTTP:
486+ schemeStr = " http" ;
487+ break ;
488+ }
489+ } else {
490+ schemeStr = " unspecified" ;
491+ }
492+ BD_MON_TABLE_ROW (" Scheme" , schemeStr);
493+
494+ BD_MON_TABLE_ROW (" Bucket" , s3Settings.GetBucket ());
495+
496+ // Helper function to mask sensitive data
497+ auto maskSensitive = [](const TString& value) -> TString {
498+ if (value.empty ()) {
499+ return value;
500+ }
501+ if (value.size () <= 12 ) {
502+ return TString (4 , ' *' );
503+ }
504+ return value.substr (0 , 2 ) + " ****" + value.substr (value.size () - 2 );
505+ };
506+ BD_MON_TABLE_ROW (" AccessKey" , maskSensitive (s3Settings.GetAccessKey ()));
507+ BD_MON_TABLE_ROW (" SecretKey" , maskSensitive (s3Settings.GetSecretKey ()));
508+
509+ BD_MON_TABLE_ROW_WITH_FORMATTER (s3Settings, Region, id_formatter);
510+ BD_MON_TABLE_ROW_WITH_FORMATTER (s3Settings, StorageClass, Ydb::Export::ExportToS3Settings::StorageClass_Name);
511+ BD_MON_TABLE_ROW_WITH_FORMATTER (s3Settings, UseVirtualAddressing, ToString);
512+ BD_MON_TABLE_ROW_WITH_FORMATTER (s3Settings, VerifySSL, ToString);
513+ BD_MON_TABLE_ROW_WITH_FORMATTER (s3Settings, ProxyHost, id_formatter);
514+ BD_MON_TABLE_ROW_WITH_FORMATTER (s3Settings, ProxyPort, id_formatter);
515+ BD_MON_TABLE_ROW_WITH_FORMATTER (s3Settings, ConnectionTimeoutMs, id_formatter);
516+ BD_MON_TABLE_ROW_WITH_FORMATTER (s3Settings, RequestTimeoutMs, id_formatter);
517+ BD_MON_TABLE_ROW_WITH_FORMATTER (s3Settings, HttpRequestTimeoutMs, id_formatter);
518+ BD_MON_TABLE_ROW_WITH_FORMATTER (s3Settings, ExecutorThreadsCount, id_formatter);
519+ BD_MON_TABLE_ROW_WITH_FORMATTER (s3Settings, MaxConnectionsCount, id_formatter);
520+ }
521+ }
522+ }
523+ }
524+ }
525+
526+ return true ;
527+ }
528+
529+ void Complete (const TActorContext&) override {
530+ TActivationContext::Send (new IEventHandle (Request->Sender , Self->SelfId (), new NMon::TEvRemoteHttpInfoRes (
531+ Stream.Str ()), 0 , Request->Cookie ));
532+ }
533+ };
534+
383535 bool TBlobDepot::OnRenderAppHtmlPage (NMon::TEvRemoteHttpInfo::TPtr ev, const TActorContext&) {
384536 if (!Executor () || !Executor ()->GetStats ().IsActive ) {
385537 return false ;
@@ -399,6 +551,9 @@ namespace NKikimr::NBlobDepot {
399551 if (page == " data" ) {
400552 Execute (std::make_unique<TTxMonData>(this , ev));
401553 return true ;
554+ } if (page == " s3config" ) {
555+ Execute (std::make_unique<TTxMonS3Config>(this , ev));
556+ return true ;
402557 } else {
403558 Send (ev->Sender , new NMon::TEvRemoteBinaryInfoRes (TStringBuilder ()
404559 << " HTTP/1.1 403 Page not found\r\n "
@@ -418,6 +573,10 @@ namespace NKikimr::NBlobDepot {
418573
419574 void TBlobDepot::RenderMainPage (IOutputStream& s) {
420575 HTML (s) {
576+ if (S3Manager) {
577+ s << " <a href='app?TabletID=" << TabletID () << " &page=s3config'>S3 config</a><br>" ;
578+ }
579+
421580 s << " <a href='app?TabletID=" << TabletID () << " &page=data'>Contained data</a><br>" ;
422581
423582 s << R"( <script>
0 commit comments