1 /*  $Id: adm_cmds.cpp 574016 2018-11-05 16:55:15Z sadyrovr $
2  * ===========================================================================
3  *
4  *                            PUBLIC DOMAIN NOTICE
5  *               National Center for Biotechnology Information
6  *
7  *  This software/database is a "United States Government Work" under the
8  *  terms of the United States Copyright Act.  It was written as part of
9  *  the author's official duties as a United States Government employee and
10  *  thus cannot be copyrighted.  This software/database is freely available
11  *  to the public for use. The National Library of Medicine and the U.S.
12  *   Government have not placed any restriction on its use or reproduction.
13  *
14  *  Although all reasonable efforts have been taken to ensure the accuracy
15  *  and reliability of the software and data, the NLM and the U.S.
16  *  Government do not and cannot warrant the performance or results that
17  *  may be obtained by using this software or data. The NLM and the U.S.
18  *  Government disclaim all warranties, express or implied, including
19  *  warranties of performance, merchantability or fitness for any particular
20  *  purpose.
21  *
22  *  Please cite the author in any work or product based on this material.
23  *
24  * ===========================================================================
25  *
26  * Authors:  Dmitry Kazimirov
27  *
28  * File Description: Miscellaneous commands of the grid_cli application
29  *                   (the ones that are not specific to NetCache, NetSchedule,
30  *                   or worker nodes).
31  *
32  */
33 
34 #include <ncbi_pch.hpp>
35 
36 #include "util.hpp"
37 #include "ns_cmd_impl.hpp"
38 
39 USING_NCBI_SCOPE;
40 
SetUp_AdminCmd(CGridCommandLineInterfaceApp::EAdminCmdSeverity cmd_severity)41 void CGridCommandLineInterfaceApp::SetUp_AdminCmd(
42         CGridCommandLineInterfaceApp::EAdminCmdSeverity cmd_severity)
43 {
44     // For commands that accept only one type of server.
45     switch (IsOptionAccepted(eNetCache, OPTION_N(0)) |
46             IsOptionAccepted(eNetSchedule, OPTION_N(1)) |
47             IsOptionAccepted(eWorkerNode, OPTION_N(2)) |
48             IsOptionAccepted(eNetStorage, OPTION_N(3))) {
49     case OPTION_N(0): // eNetCache
50         SetUp_NetCacheAdminCmd(cmd_severity);
51         return;
52 
53     case OPTION_N(1): // eNetSchedule
54         SetUp_NetScheduleCmd(eNetScheduleAdmin, cmd_severity, false);
55         return;
56 
57     case OPTION_N(2): // eWorkerNode
58         SetUp_NetScheduleCmd(eWorkerNodeAdmin, cmd_severity, false);
59         return;
60 
61     case OPTION_N(3): // eNetStorage
62         SetUp_NetStorageCmd(eNetStorageAdmin, cmd_severity);
63         return;
64 
65     default:
66         break;
67     }
68 
69     // For commands that accept multiple types of servers.
70     switch (IsOptionExplicitlySet(eNetCache, OPTION_N(0)) |
71             IsOptionExplicitlySet(eNetSchedule, OPTION_N(1)) |
72             IsOptionExplicitlySet(eWorkerNode, OPTION_N(2)) |
73             IsOptionExplicitlySet(eNetStorage, OPTION_N(3))) {
74     case OPTION_N(0): // eNetCache
75         SetUp_NetCacheAdminCmd(cmd_severity);
76         return;
77 
78     case OPTION_N(1): // eNetSchedule
79         SetUp_NetScheduleCmd(eNetScheduleAdmin, cmd_severity, false);
80         return;
81 
82     case OPTION_N(2): // eWorkerNode
83         SetUp_NetScheduleCmd(eWorkerNodeAdmin, cmd_severity, false);
84         return;
85 
86     case OPTION_N(3): // eNetStorage
87         SetUp_NetStorageCmd(eNetStorageAdmin, cmd_severity);
88         return;
89 
90     case 0: // No options specified
91         NCBI_THROW(CArgException, eNoValue, "this command requires "
92                 "a service name or a server address");
93 
94     default: // A combination of options
95         NCBI_THROW(CArgException, eNoValue, "this command works "
96                 "with only one type of server at a time");
97     }
98 }
99 
Cmd_ServerInfo()100 int CGridCommandLineInterfaceApp::Cmd_ServerInfo()
101 {
102     CNetService service;
103 
104     SetUp_AdminCmd(eReadOnlyAdminCmd);
105 
106     bool server_version_key = true;
107 
108     switch (m_APIClass) {
109     case eNetCacheAdmin:
110         service = m_NetCacheAPI.GetService();
111         break;
112 
113     case eNetScheduleAdmin:
114     case eWorkerNodeAdmin:
115         server_version_key = false;
116         service = m_NetScheduleAPI.GetService();
117         break;
118 
119     case eNetStorageAdmin:
120         return PrintNetStorageServerInfo();
121 
122     default:
123         return 2;
124     }
125 
126     if (m_Opts.output_format == eJSON)
127         g_PrintJSON(stdout, g_ServerInfoToJson(service,
128                 server_version_key));
129     else if (m_Opts.output_format == eRaw)
130         service.PrintCmdOutput("VERSION", NcbiCout,
131                 CNetService::eSingleLineOutput,
132                 CNetService::eIncludePenalized);
133     else {
134         bool print_server_address = service.IsLoadBalanced();
135 
136         for (CNetServiceIterator it =
137                 service.Iterate(CNetService::eIncludePenalized); it; ++it) {
138             if (print_server_address)
139                 printf("[%s]\n", (*it).GetServerAddress().c_str());
140 
141             CNetServerInfo server_info((*it).GetServerInfo());
142 
143             string attr_name, attr_value;
144 
145             while (server_info.GetNextAttribute(attr_name, attr_value))
146                 printf("%s: %s\n", attr_name.c_str(), attr_value.c_str());
147 
148             if (print_server_address)
149                 printf("\n");
150         }
151     }
152 
153     return 0;
154 }
155 
Cmd_Stats()156 int CGridCommandLineInterfaceApp::Cmd_Stats()
157 {
158     SetUp_AdminCmd(eReadOnlyAdminCmd);
159 
160     switch (m_APIClass) {
161     case eNetCacheAdmin:
162         m_NetCacheAdmin.PrintStat(NcbiCout, m_Opts.aggregation_interval,
163                 !IsOptionSet(ePreviousInterval) ?
164                         CNetCacheAdmin::eReturnCurrentPeriod :
165                         CNetCacheAdmin::eReturnCompletePeriod);
166         return 0;
167 
168     case eNetScheduleAdmin:
169         return PrintNetScheduleStats();
170 
171     case eWorkerNodeAdmin:
172         if (m_Opts.output_format == eJSON)
173             g_PrintJSON(stdout, g_WorkerNodeInfoToJson(
174                     m_NetScheduleAPI.GetService().Iterate().GetServer()));
175         else
176             m_NetScheduleAdmin.PrintServerStatistics(NcbiCout);
177         return 0;
178 
179     default:
180         return 2;
181     }
182 }
183 
Cmd_Health()184 int CGridCommandLineInterfaceApp::Cmd_Health()
185 {
186     SetUp_AdminCmd(eReadOnlyAdminCmd);
187 
188     switch (m_APIClass) {
189     case eNetCacheAdmin:
190         m_NetCacheAdmin.PrintHealth(NcbiCout);
191         return 0;
192 
193     case eNetScheduleAdmin:
194         m_NetScheduleAdmin.PrintHealth(NcbiCout);
195         return 0;
196 
197     default:
198         return 2;
199     }
200 }
201 
202 #define CHECK_FAILED_RETVAL 10
203 
NetCacheSanityCheck()204 int CGridCommandLineInterfaceApp::NetCacheSanityCheck()
205 {
206     // functionality test
207 
208     const char test_data[] = "A quick brown fox, jumps over lazy dog.";
209     const char test_data2[] = "Test 2.";
210     string key = m_NetCacheAPI.PutData(test_data, sizeof(test_data));
211 
212     if (key.empty()) {
213         NcbiCerr << "Failed to put data. " << NcbiEndl;
214         return CHECK_FAILED_RETVAL;
215     }
216     NcbiCout << key << NcbiEndl;
217 
218     char data_buf[1024];
219 
220     size_t blob_size = m_NetCacheAPI.GetBlobSize(key);
221 
222     if (blob_size != sizeof(test_data)) {
223         NcbiCerr << "Failed to retrieve data size." << NcbiEndl;
224         return CHECK_FAILED_RETVAL;
225     }
226 
227     unique_ptr<IReader> reader(m_NetCacheAPI.GetData(key, &blob_size,
228             nc_caching_mode = CNetCacheAPI::eCaching_Disable));
229 
230     if (reader.get() == 0) {
231         NcbiCerr << "Failed to read data." << NcbiEndl;
232         return CHECK_FAILED_RETVAL;
233     }
234 
235     reader->Read(data_buf, 1024);
236     int res = strcmp(data_buf, test_data);
237     if (res != 0) {
238         NcbiCerr << "Could not read data." << NcbiEndl <<
239             "Server returned:" << NcbiEndl << data_buf << NcbiEndl <<
240             "Expected:" << NcbiEndl << test_data << NcbiEndl;
241 
242         return CHECK_FAILED_RETVAL;
243     }
244     reader.reset(0);
245 
246     {{
247         unique_ptr<IEmbeddedStreamWriter> wrt(m_NetCacheAPI.PutData(&key));
248         size_t bytes_written;
249         wrt->Write(test_data2, sizeof(test_data2), &bytes_written);
250         wrt->Close();
251     }}
252 
253     memset(data_buf, 0xff, sizeof(data_buf));
254     reader.reset(m_NetCacheAPI.GetReader(key, &blob_size,
255             nc_caching_mode = CNetCacheAPI::eCaching_Disable));
256     reader->Read(data_buf, 1024);
257     res = strcmp(data_buf, test_data2);
258     if (res != 0) {
259         NcbiCerr << "Could not read updated data." << NcbiEndl <<
260             "Server returned:" << NcbiEndl << data_buf << NcbiEndl <<
261             "Expected:" << NcbiEndl << test_data2 << NcbiEndl;
262 
263         return CHECK_FAILED_RETVAL;
264     }
265 
266     return 0;
267 }
268 
NetScheduleSanityCheck()269 int CGridCommandLineInterfaceApp::NetScheduleSanityCheck()
270 {
271     if (!IsOptionSet(eQueue)) {
272         if (!IsOptionSet(eQueueClass)) {
273             fprintf(stderr, GRID_APP_NAME ": '--" QUEUE_OPTION
274                     "' or '--" QUEUE_CLASS_OPTION
275                     "' (or both) must be specified.\n");
276             return 2;
277         }
278         m_Opts.queue = NETSCHEDULE_CHECK_QUEUE;
279     }
280     if (IsOptionSet(eQueueClass))
281         m_NetScheduleAdmin.CreateQueue(m_Opts.queue, m_Opts.queue_class);
282 
283     CNetScheduleAdmin::TQueueList server_queues;
284 
285     m_NetScheduleAdmin.GetQueueList(server_queues);
286 
287     ITERATE(CNetScheduleAdmin::TQueueList, it, server_queues) {
288         list<string>::const_iterator queue(it->queues.begin());
289         for (;;) {
290             if (queue == it->queues.end()) {
291                 fprintf(stderr, "The queue '%s' is not available on '%s'.\n",
292                         m_Opts.queue.c_str(),
293                         it->server.GetServerAddress().c_str());
294                 return 4;
295             }
296             if (*queue == m_Opts.queue)
297                 break;
298             ++queue;
299         }
300     }
301 
302     SetUp_NetScheduleCmd(eNetScheduleExecutor, eReadOnlyAdminCmd, false);
303 
304     const string input = "Hello ";
305     const string output = "DONE ";
306     CNetScheduleJob job(input);
307     m_NetScheduleSubmitter.SubmitJob(job);
308 
309     for (;;) {
310         CNetScheduleJob job1;
311         bool job_exists = m_NetScheduleExecutor.GetJob(job1, 5);
312         if (job_exists) {
313             if (job1.job_id != job.job_id)
314                 m_NetScheduleExecutor.ReturnJob(job1);
315             else {
316                 if (job1.input != job.input) {
317                     job1.error_msg = "Job's (" + job1.job_id +
318                         ") input does not match.(" + job.input +
319                         ") ["+ job1.input +"]";
320                     m_NetScheduleExecutor.PutFailure(job1);
321                 } else {
322                     job1.output = output;
323                     job1.ret_code = 0;
324                     m_NetScheduleExecutor.PutResult(job1);
325                 }
326                 break;
327             }
328         }
329     }
330 
331     bool check_again = true;
332     int ret = 0;
333     string err;
334     while (check_again) {
335         check_again = false;
336 
337         CNetScheduleAPI::EJobStatus status = m_NetScheduleSubmitter.GetJobDetails(job);
338         switch(status) {
339 
340         case CNetScheduleAPI::eJobNotFound:
341             ret = 10;
342             err = "Job (" + job.job_id +") is lost.";
343             break;
344         case CNetScheduleAPI::eCanceled:
345             ret = 12;
346             err = "Job (" + job.job_id +") is canceled.";
347             break;
348         case CNetScheduleAPI::eFailed:
349             ret = 13;
350             break;
351         case CNetScheduleAPI::eDone:
352             if (job.ret_code != 0) {
353                 ret = job.ret_code;
354                 err = "Job (" + job.job_id +") is done, but retcode is not zero.";
355             } else if (job.output != output) {
356                 err = "Job (" + job.job_id + ") is done, output does not match.("
357                     + output + ") ["+ job.output +"]";
358                 ret = 14;
359             } else if (job.input != input) {
360                 err = "Job (" + job.job_id +") is done, input does not match.("
361                     + input + ") ["+ job.input +"]";
362                 ret = 15;
363             }
364             break;
365         case CNetScheduleAPI::ePending:
366         case CNetScheduleAPI::eRunning:
367         default:
368             check_again = true;
369         }
370     }
371 
372     if (ret != 0)
373         ERR_POST(err);
374 
375     if (IsOptionSet(eQueueClass))
376         m_NetScheduleAdmin.DeleteQueue(m_Opts.queue);
377 
378     return ret;
379 }
380 
Cmd_SanityCheck()381 int CGridCommandLineInterfaceApp::Cmd_SanityCheck()
382 {
383     SetUp_AdminCmd(eAdminCmdWithSideEffects);
384 
385     switch (m_APIClass) {
386     case eNetCacheAdmin:
387         return NetCacheSanityCheck();
388 
389     case eNetScheduleAdmin:
390         return NetScheduleSanityCheck();
391 
392     default:
393         return 2;
394     }
395 }
396 
Cmd_GetConf()397 int CGridCommandLineInterfaceApp::Cmd_GetConf()
398 {
399     SetUp_AdminCmd(eReadOnlyAdminCmd);
400 
401     switch (m_APIClass) {
402     case eNetCacheAdmin:
403         m_NetCacheAdmin.PrintConfig(NcbiCout);
404         return 0;
405 
406     case eNetScheduleAdmin:
407     case eWorkerNodeAdmin:
408         m_NetScheduleAdmin.PrintConf(NcbiCout);
409         return 0;
410 
411     case eNetStorageAdmin:
412         return PrintNetStorageServerConfig();
413 
414     default:
415         return 2;
416     }
417 }
418 
Cmd_Reconf()419 int CGridCommandLineInterfaceApp::Cmd_Reconf()
420 {
421     SetUp_AdminCmd(eAdminCmdWithSideEffects);
422 
423     switch (m_APIClass) {
424     case eNetCacheAdmin:
425         m_NetCacheAdmin.ReloadServerConfig(IsOptionExplicitlySet(eMirror) ?
426                 CNetCacheAdmin::eMirrorReload : CNetCacheAdmin::eCompleteReload);
427         return 0;
428 
429     case eNetScheduleAdmin:
430         g_PrintJSON(stdout, g_ReconfAndReturnJson(m_NetScheduleAPI));
431         return 0;
432 
433     case eNetStorageAdmin:
434         return ReconfigureNetStorageServer();
435 
436     default:
437         return 2;
438     }
439 }
440 
Cmd_Drain()441 int CGridCommandLineInterfaceApp::Cmd_Drain()
442 {
443     SetUp_AdminCmd(eAdminCmdWithSideEffects);
444 
445     switch (m_APIClass) {
446     case eNetScheduleAdmin:
447         m_NetScheduleAdmin.SwitchToDrainMode(m_Opts.on_off_switch);
448         return 0;
449 
450     default:
451         return 2;
452     }
453     return 0;
454 }
455 
NetSchedule_SuspendResume(bool suspend)456 void CGridCommandLineInterfaceApp::NetSchedule_SuspendResume(bool suspend)
457 {
458     if (IsOptionAcceptedAndSetImplicitly(eQueue)) {
459         NCBI_THROW(CArgException, eNoValue,
460                 "missing option '--" QUEUE_OPTION "'");
461     }
462 
463     if (suspend)
464         g_SuspendNetSchedule(m_NetScheduleAPI, IsOptionSet(ePullback));
465     else
466         g_ResumeNetSchedule(m_NetScheduleAPI);
467 }
468 
Cmd_Suspend()469 int CGridCommandLineInterfaceApp::Cmd_Suspend()
470 {
471     SetUp_AdminCmd(eAdminCmdWithSideEffects);
472 
473     switch (m_APIClass) {
474     case eNetScheduleAdmin:
475         NetSchedule_SuspendResume(true);
476         return 0;
477 
478     case eWorkerNodeAdmin:
479         {
480             CNetServer worker_node =
481                     m_NetScheduleAPI.GetService().Iterate().GetServer();
482             g_SuspendWorkerNode(worker_node,
483                     IsOptionSet(ePullback), m_Opts.timeout);
484             if (IsOptionSet(eWaitForJobCompletion)) {
485                 string output_line;
486                 for (;;) {
487                     unsigned jobs_running = 0;
488                     CNetServerMultilineCmdOutput stat_output(
489                             worker_node.ExecWithRetry("STAT", true));
490                     while (stat_output.ReadLine(output_line))
491                         if (NStr::StartsWith(output_line, "Jobs Running: "))
492                             jobs_running = NStr::StringToUInt(
493                                     output_line.c_str() +
494                                     sizeof("Jobs Running: ") - 1);
495                     if (jobs_running == 0)
496                         break;
497                     SleepMilliSec(500);
498                 }
499             }
500         }
501         return 0;
502 
503     default:
504         return 2;
505     }
506 }
507 
Cmd_Resume()508 int CGridCommandLineInterfaceApp::Cmd_Resume()
509 {
510     SetUp_AdminCmd(eAdminCmdWithSideEffects);
511 
512     switch (m_APIClass) {
513     case eNetScheduleAdmin:
514         NetSchedule_SuspendResume(false);
515         return 0;
516 
517     case eWorkerNodeAdmin:
518         g_ResumeWorkerNode(m_NetScheduleAPI.GetService().Iterate().GetServer());
519         return 0;
520 
521     default:
522         return 2;
523     }
524 }
525 
Cmd_Shutdown()526 int CGridCommandLineInterfaceApp::Cmd_Shutdown()
527 {
528     SetUp_AdminCmd(eAdminCmdWithSideEffects);
529 
530     switch (m_APIClass) {
531     case eNetCacheAdmin:
532         m_NetCacheAdmin.ShutdownServer(IsOptionSet(eDrain) ?
533                 CNetCacheAdmin::eDrain : CNetCacheAdmin::eNormalShutdown);
534         return 0;
535 
536     case eNetScheduleAdmin:
537     case eWorkerNodeAdmin:
538         switch (IsOptionSet(eNow, OPTION_N(0)) |
539                 IsOptionSet(eDie, OPTION_N(1)) |
540                 IsOptionSet(eDrain, OPTION_N(2))) {
541         case 0: // No additional options.
542             m_NetScheduleAdmin.ShutdownServer();
543             return 0;
544 
545         case OPTION_N(0): // eNow is set.
546             m_NetScheduleAdmin.ShutdownServer(
547                 CNetScheduleAdmin::eShutdownImmediate);
548             return 0;
549 
550         case OPTION_N(1): // eDie is set.
551             m_NetScheduleAdmin.ShutdownServer(CNetScheduleAdmin::eDie);
552             return 0;
553 
554         case OPTION_N(2): // eDrain is set.
555             m_NetScheduleAdmin.ShutdownServer(CNetScheduleAdmin::eDrain);
556             return 0;
557 
558         default: // A combination of the above options.
559             fprintf(stderr, GRID_APP_NAME ": options '--" NOW_OPTION "', '--"
560                     DIE_OPTION "', and '--" DRAIN_OPTION
561                     "' are mutually exclusive.\n");
562             return 2;
563         }
564 
565     case eNetStorageAdmin:
566         return ShutdownNetStorageServer();
567 
568     default:
569         return 2;
570     }
571 }
572 
573 namespace {
574     class CAdjustableTableColumn {
575     public:
576         enum {
577             fAlignRight = 1,
578             fLastColumn = 2
579         };
CAdjustableTableColumn(int min_width=0,int spacing=1,unsigned mode=0)580         CAdjustableTableColumn(int min_width = 0,
581                 int spacing = 1,
582                 unsigned mode = 0) :
583             m_Width(min_width),
584             m_Spacing(spacing),
585             m_Mode(mode)
586         {
587         }
AddCell(const string & text)588         void AddCell(const string& text)
589         {
590             int length = (int)text.length();
591             if (m_Width < length)
592                 m_Width = length;
593             m_Cells.push_back(text);
594         }
PrintCell(size_t row)595         void PrintCell(size_t row)
596         {
597             switch (m_Mode & (fAlignRight | fLastColumn)) {
598             default:
599                 printf("%-*s", m_Width + m_Spacing, m_Cells[row].c_str());
600                 break;
601             case fAlignRight:
602                 printf("%*s%*s", m_Width, m_Cells[row].c_str(), m_Spacing, "");
603                 break;
604             case fLastColumn:
605                 printf("%s\n", m_Cells[row].c_str());
606                 break;
607             case fAlignRight | fLastColumn:
608                 printf("%*s\n", m_Width, m_Cells[row].c_str());
609             }
610         }
PrintRule()611         void PrintRule()
612         {
613             const static char eight_dashes[] = "--------";
614 
615             int width = m_Width;
616             while (width > 8) {
617                 printf("%s", eight_dashes);
618                 width -= 8;
619             }
620 
621             if ((m_Mode & fLastColumn) == 0)
622                 printf("%-*.*s", width + m_Spacing, width, eight_dashes);
623             else
624                 printf("%.*s\n", width, eight_dashes);
625         }
GetHeight() const626         size_t GetHeight() const {return m_Cells.size();}
627 
628     private:
629         int m_Width;
630         int m_Spacing;
631         unsigned m_Mode;
632         vector<string> m_Cells;
633     };
634 }
635 
636 #define NCBI_DOMAIN ".ncbi.nlm.nih.gov"
637 
Cmd_Discover()638 int CGridCommandLineInterfaceApp::Cmd_Discover()
639 {
640     CNetService service = CNetService::Create("discovery", m_Opts.service_name, m_Opts.auth);
641 
642     CAdjustableTableColumn ipv4_column(0, 2);
643     ipv4_column.AddCell("IPv4 Address");
644 
645     CAdjustableTableColumn hostname_column(0, 2);
646     if (!IsOptionSet(eNoDNSLookup))
647         hostname_column.AddCell("Hostname");
648 
649     CAdjustableTableColumn port_column(0, 2,
650             CAdjustableTableColumn::fAlignRight);
651     port_column.AddCell("Port");
652 
653     CAdjustableTableColumn rating_column(7, 0,
654             CAdjustableTableColumn::fAlignRight |
655             CAdjustableTableColumn::fLastColumn);
656     rating_column.AddCell("Rating");
657 
658     for (CNetServiceIterator it =
659             service.Iterate(CNetService::eIncludePenalized); it; ++it) {
660         CNetServer server(it.GetServer());
661         unsigned ipv4 = server.GetHost();
662         string ipv4_str = CSocketAPI::ntoa(ipv4);
663         ipv4_column.AddCell(ipv4_str);
664         if (!IsOptionSet(eNoDNSLookup)) {
665             string hostname = CSocketAPI::gethostbyaddr(ipv4, eOff);
666             if (hostname.empty() || hostname == ipv4_str)
667                 hostname = "***DNS lookup failed***";
668             else if (NStr::EndsWith(hostname, NCBI_DOMAIN))
669                 hostname.erase(hostname.length() - (sizeof(NCBI_DOMAIN) - 1));
670             hostname_column.AddCell(hostname);
671         }
672         port_column.AddCell(NStr::UIntToString(server.GetPort()));
673         rating_column.AddCell(NStr::DoubleToString(it.GetRate(), 3));
674     }
675 
676     for (unsigned row = 0; row < ipv4_column.GetHeight(); ++row) {
677         ipv4_column.PrintCell(row);
678         if (!IsOptionSet(eNoDNSLookup))
679             hostname_column.PrintCell(row);
680         port_column.PrintCell(row);
681         rating_column.PrintCell(row);
682         if (row == 0) {
683             ipv4_column.PrintRule();
684             if (!IsOptionSet(eNoDNSLookup))
685                 hostname_column.PrintRule();
686             port_column.PrintRule();
687             rating_column.PrintRule();
688         }
689     }
690 
691     return 0;
692 }
693 
Cmd_Exec()694 int CGridCommandLineInterfaceApp::Cmd_Exec()
695 {
696     SetUp_AdminCmd(eAdminCmdWithSideEffects);
697 
698     CNetService service;
699 
700     switch (m_APIClass) {
701     case eNetCacheAdmin:
702         service = m_NetCacheAPI.GetService();
703         break;
704 
705     case eNetScheduleAdmin:
706         service = m_NetScheduleAPI.GetService();
707         break;
708 
709     default:
710         return 2;
711     }
712 
713     if (m_Opts.output_format == eRaw)
714         service.PrintCmdOutput(m_Opts.command, NcbiCout,
715                 IsOptionSet(eMultiline) ? CNetService::eMultilineOutput :
716                 CNetService::eSingleLineOutput);
717     else // Output format is eJSON.
718         g_PrintJSON(stdout, g_ExecAnyCmdToJson(service,
719                 m_Opts.command, IsOptionSet(eMultiline)));
720 
721     return 0;
722 }
723